498 lines
12 KiB
Go
498 lines
12 KiB
Go
/*
|
|
|--------------------------------------------------------------------------
|
|
| Codec
|
|
|--------------------------------------------------------------------------
|
|
|
|
|
| Name:
|
|
| - Codec
|
|
|
|
|
| Purpose:
|
|
| - Provide unified encoding and decoding capabilities using Base8,
|
|
| Base10, Base16, Base32, Base64, and Sqids with centralized
|
|
| validation, dispatching, transformation handling, and structured
|
|
| result processing.
|
|
|
|
|
| Guidelines:
|
|
| - Validate all required parameters before execution.
|
|
| - Reject unsupported operations and formats.
|
|
| - Return standardized structured responses.
|
|
| - Prevent application crashes through safe error handling.
|
|
|
|
|
| Examples:
|
|
| - ObrimCodec("encode", config)
|
|
| - ObrimCodec("decode", config)
|
|
|
|
|
|--------------------------------------------------------------------------
|
|
*/
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| Credit
|
|
|--------------------------------------------------------------------------
|
|
|
|
|
| Contributor:
|
|
| - Rajon Ahmed
|
|
|
|
|
|--------------------------------------------------------------------------
|
|
*/
|
|
|
|
package codec
|
|
|
|
import (
|
|
"encoding/base32"
|
|
"encoding/base64"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"math/big"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/sqids/sqids-go"
|
|
)
|
|
|
|
// Function Purpose: Standardized utility response structure.
|
|
type ObrimCodecResponse struct {
|
|
Status bool `json:"status"`
|
|
Code string `json:"code"`
|
|
Payload map[string]any `json:"payload"`
|
|
}
|
|
|
|
// Function Purpose: Validate configuration, dispatch codec operations, and return structured results.
|
|
func ObrimCodec(operation string, config map[string]any) (result ObrimCodecResponse) {
|
|
defer func() {
|
|
if recover() != nil {
|
|
result = ObrimCodecResponse{
|
|
Status: false,
|
|
Code: "FAILED_PANIC",
|
|
Payload: nil,
|
|
}
|
|
}
|
|
}()
|
|
|
|
if validation := obrimCodecValidation(operation, config); validation != nil {
|
|
return *validation
|
|
}
|
|
|
|
return obrimCodecDispatch(operation, config)
|
|
}
|
|
|
|
// Function Purpose: Validate required parameters, supported operations, formats, and parameter types.
|
|
func obrimCodecValidation(operation string, config map[string]any) *ObrimCodecResponse {
|
|
if operation != "encode" && operation != "decode" {
|
|
return &ObrimCodecResponse{
|
|
Status: false,
|
|
Code: "FAILED_UNSUPPORTED_OPERATION",
|
|
Payload: nil,
|
|
}
|
|
}
|
|
|
|
// Logic: Validate format.
|
|
formatValue, exists := config["format"]
|
|
if !exists {
|
|
return &ObrimCodecResponse{
|
|
Status: false,
|
|
Code: "FAILED_MISSING_FORMAT",
|
|
Payload: nil,
|
|
}
|
|
}
|
|
|
|
format, ok := formatValue.(string)
|
|
if !ok {
|
|
return &ObrimCodecResponse{
|
|
Status: false,
|
|
Code: "FAILED_INVALID_FORMAT_TYPE",
|
|
Payload: nil,
|
|
}
|
|
}
|
|
|
|
switch format {
|
|
case "base8", "base10", "base16", "base32", "base64", "sqids":
|
|
default:
|
|
return &ObrimCodecResponse{
|
|
Status: false,
|
|
Code: "FAILED_UNSUPPORTED_FORMAT",
|
|
Payload: nil,
|
|
}
|
|
}
|
|
|
|
// Logic: Validate data.
|
|
dataValue, exists := config["data"]
|
|
if !exists {
|
|
return &ObrimCodecResponse{
|
|
Status: false,
|
|
Code: "FAILED_MISSING_DATA",
|
|
Payload: nil,
|
|
}
|
|
}
|
|
|
|
switch dataValue.(type) {
|
|
case string, []byte:
|
|
default:
|
|
return &ObrimCodecResponse{
|
|
Status: false,
|
|
Code: "FAILED_INVALID_DATA_TYPE",
|
|
Payload: nil,
|
|
}
|
|
}
|
|
|
|
// Logic: Validate charset.
|
|
charsetValue, exists := config["charset"]
|
|
if !exists {
|
|
return &ObrimCodecResponse{
|
|
Status: false,
|
|
Code: "FAILED_MISSING_CHARSET",
|
|
Payload: nil,
|
|
}
|
|
}
|
|
|
|
if _, ok := charsetValue.(string); !ok {
|
|
return &ObrimCodecResponse{
|
|
Status: false,
|
|
Code: "FAILED_INVALID_CHARSET_TYPE",
|
|
Payload: nil,
|
|
}
|
|
}
|
|
|
|
// Logic: Validate salt.
|
|
saltValue, exists := config["salt"]
|
|
if !exists {
|
|
return &ObrimCodecResponse{
|
|
Status: false,
|
|
Code: "FAILED_MISSING_SALT",
|
|
Payload: nil,
|
|
}
|
|
}
|
|
|
|
if _, ok := saltValue.(string); !ok {
|
|
return &ObrimCodecResponse{
|
|
Status: false,
|
|
Code: "FAILED_INVALID_SALT_TYPE",
|
|
Payload: nil,
|
|
}
|
|
}
|
|
|
|
if format == "sqids" {
|
|
lengthValue, exists := config["length"]
|
|
if !exists {
|
|
return &ObrimCodecResponse{
|
|
Status: false,
|
|
Code: "FAILED_MISSING_LENGTH",
|
|
Payload: nil,
|
|
}
|
|
}
|
|
|
|
switch lengthValue.(type) {
|
|
case int, int8, int16, int32, int64:
|
|
default:
|
|
return &ObrimCodecResponse{
|
|
Status: false,
|
|
Code: "FAILED_INVALID_LENGTH_TYPE",
|
|
Payload: nil,
|
|
}
|
|
}
|
|
} else {
|
|
caseValue, exists := config["case"]
|
|
if !exists {
|
|
return &ObrimCodecResponse{
|
|
Status: false,
|
|
Code: "FAILED_MISSING_CASE",
|
|
Payload: nil,
|
|
}
|
|
}
|
|
|
|
caseRule, ok := caseValue.(string)
|
|
if !ok {
|
|
return &ObrimCodecResponse{
|
|
Status: false,
|
|
Code: "FAILED_INVALID_CASE_TYPE",
|
|
Payload: nil,
|
|
}
|
|
}
|
|
|
|
if caseRule != "lower" && caseRule != "upper" {
|
|
return &ObrimCodecResponse{
|
|
Status: false,
|
|
Code: "FAILED_INVALID_CASE_VALUE",
|
|
Payload: nil,
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Function Purpose: Route validated requests to the appropriate codec implementation.
|
|
func obrimCodecDispatch(operation string, config map[string]any) ObrimCodecResponse {
|
|
format := config["format"].(string)
|
|
|
|
var (
|
|
// Logic:
|
|
value string
|
|
err error
|
|
)
|
|
|
|
switch operation {
|
|
case "encode":
|
|
switch format {
|
|
case "base8":
|
|
value, err = obrimCodecEncodeBase8(config)
|
|
case "base10":
|
|
value, err = obrimCodecEncodeBase10(config)
|
|
case "base16":
|
|
value, err = obrimCodecEncodeBase16(config)
|
|
case "base32":
|
|
value, err = obrimCodecEncodeBase32(config)
|
|
case "base64":
|
|
value, err = obrimCodecEncodeBase64(config)
|
|
case "sqids":
|
|
value, err = obrimCodecEncodeSqids(config)
|
|
}
|
|
|
|
case "decode":
|
|
switch format {
|
|
case "base8":
|
|
value, err = obrimCodecDecodeBase8(config)
|
|
case "base10":
|
|
value, err = obrimCodecDecodeBase10(config)
|
|
case "base16":
|
|
value, err = obrimCodecDecodeBase16(config)
|
|
case "base32":
|
|
value, err = obrimCodecDecodeBase32(config)
|
|
case "base64":
|
|
value, err = obrimCodecDecodeBase64(config)
|
|
case "sqids":
|
|
value, err = obrimCodecDecodeSqids(config)
|
|
}
|
|
}
|
|
|
|
if err != nil {
|
|
return ObrimCodecResponse{
|
|
Status: false,
|
|
Code: "FAILED_OPERATION",
|
|
Payload: nil,
|
|
}
|
|
}
|
|
|
|
return ObrimCodecResponse{
|
|
Status: true,
|
|
Code: "SUCCESS_OPERATION",
|
|
Payload: map[string]any{
|
|
"operation": operation,
|
|
"format": format,
|
|
"value": value,
|
|
},
|
|
}
|
|
}
|
|
|
|
// Function Purpose: Encode data using Base8 encoding.
|
|
func obrimCodecEncodeBase8(config map[string]any) (string, error) {
|
|
return obrimCodecEncodeRadix(config, 8)
|
|
}
|
|
|
|
// Function Purpose: Encode data using Base10 encoding.
|
|
func obrimCodecEncodeBase10(config map[string]any) (string, error) {
|
|
return obrimCodecEncodeRadix(config, 10)
|
|
}
|
|
|
|
// Function Purpose: Encode data using Base16 encoding.
|
|
func obrimCodecEncodeBase16(config map[string]any) (string, error) {
|
|
value := hex.EncodeToString(obrimCodecBytes(config["data"]))
|
|
return obrimCodecApplyCase(obrimCodecTransformEncode(value, config)), nil
|
|
}
|
|
|
|
// Function Purpose: Encode data using Base32 encoding.
|
|
func obrimCodecEncodeBase32(config map[string]any) (string, error) {
|
|
value := base32.StdEncoding.EncodeToString(obrimCodecBytes(config["data"]))
|
|
return obrimCodecApplyCase(obrimCodecTransformEncode(value, config)), nil
|
|
}
|
|
|
|
// Function Purpose: Encode data using Base64 encoding.
|
|
func obrimCodecEncodeBase64(config map[string]any) (string, error) {
|
|
value := base64.StdEncoding.EncodeToString(obrimCodecBytes(config["data"]))
|
|
return obrimCodecApplyCase(obrimCodecTransformEncode(value, config)), nil
|
|
}
|
|
|
|
// Function Purpose: Encode data using Sqids encoding with minimum length enforcement.
|
|
func obrimCodecEncodeSqids(config map[string]any) (string, error) {
|
|
length := obrimCodecLength(config["length"])
|
|
|
|
s, err := sqids.New(sqids.Options{
|
|
MinLength: uint8(length),
|
|
Alphabet: config["charset"].(string),
|
|
Blocklist: []string{},
|
|
})
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
number := obrimCodecBigInt(obrimCodecBytes(config["data"])).Uint64()
|
|
|
|
value, err := s.Encode([]uint64{number})
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return obrimCodecTransformEncode(value, config), nil
|
|
}
|
|
|
|
// Function Purpose: Decode Base8 encoded data.
|
|
func obrimCodecDecodeBase8(config map[string]any) (string, error) {
|
|
return obrimCodecDecodeRadix(config, 8)
|
|
}
|
|
|
|
// Function Purpose: Decode Base10 encoded data.
|
|
func obrimCodecDecodeBase10(config map[string]any) (string, error) {
|
|
return obrimCodecDecodeRadix(config, 10)
|
|
}
|
|
|
|
// Function Purpose: Decode Base16 encoded data.
|
|
func obrimCodecDecodeBase16(config map[string]any) (string, error) {
|
|
value := obrimCodecTransformDecode(config["data"].(string), config)
|
|
|
|
decoded, err := hex.DecodeString(value)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return string(decoded), nil
|
|
}
|
|
|
|
// Function Purpose: Decode Base32 encoded data.
|
|
func obrimCodecDecodeBase32(config map[string]any) (string, error) {
|
|
value := obrimCodecTransformDecode(config["data"].(string), config)
|
|
|
|
decoded, err := base32.StdEncoding.DecodeString(strings.ToUpper(value))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return string(decoded), nil
|
|
}
|
|
|
|
// Function Purpose: Decode Base64 encoded data.
|
|
func obrimCodecDecodeBase64(config map[string]any) (string, error) {
|
|
value := obrimCodecTransformDecode(config["data"].(string), config)
|
|
|
|
decoded, err := base64.StdEncoding.DecodeString(value)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return string(decoded), nil
|
|
}
|
|
|
|
// Function Purpose: Decode Sqids encoded data using deterministic length validation.
|
|
func obrimCodecDecodeSqids(config map[string]any) (string, error) {
|
|
length := obrimCodecLength(config["length"])
|
|
|
|
s, err := sqids.New(sqids.Options{
|
|
MinLength: uint8(length),
|
|
Alphabet: config["charset"].(string),
|
|
Blocklist: []string{},
|
|
})
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
value := obrimCodecTransformDecode(config["data"].(string), config)
|
|
|
|
numbers := s.Decode(value)
|
|
if len(numbers) == 0 {
|
|
return "", fmt.Errorf("decode failed")
|
|
}
|
|
|
|
return strconv.FormatUint(numbers[0], 10), nil
|
|
}
|
|
|
|
// Function Purpose: Encode generic radix values.
|
|
func obrimCodecEncodeRadix(config map[string]any, base int) (string, error) {
|
|
value := obrimCodecBigInt(obrimCodecBytes(config["data"])).Text(base)
|
|
value = obrimCodecTransformEncode(value, config)
|
|
value = obrimCodecApplyCase(value, config["case"].(string))
|
|
return value, nil
|
|
}
|
|
|
|
// Function Purpose: Decode generic radix values.
|
|
func obrimCodecDecodeRadix(config map[string]any, base int) (string, error) {
|
|
value := obrimCodecTransformDecode(config["data"].(string), config)
|
|
|
|
number := new(big.Int)
|
|
|
|
_, ok := number.SetString(value, base)
|
|
if !ok {
|
|
return "", fmt.Errorf("invalid value")
|
|
}
|
|
|
|
return string(number.Bytes()), nil
|
|
}
|
|
|
|
// Function Purpose: Convert supported data into bytes.
|
|
func obrimCodecBytes(data any) []byte {
|
|
switch value := data.(type) {
|
|
case string:
|
|
return []byte(value)
|
|
case []byte:
|
|
return value
|
|
default:
|
|
return []byte{}
|
|
}
|
|
}
|
|
|
|
// Function Purpose: Convert bytes into big integer.
|
|
func obrimCodecBigInt(data []byte) *big.Int {
|
|
return new(big.Int).SetBytes(data)
|
|
}
|
|
|
|
// Function Purpose: Apply encoding transformations.
|
|
func obrimCodecTransformEncode(value string, config map[string]any) string {
|
|
charset := config["charset"].(string)
|
|
salt := config["salt"].(string)
|
|
|
|
return salt + charset + value
|
|
}
|
|
|
|
// Function Purpose: Reverse encoding transformations.
|
|
func obrimCodecTransformDecode(value string, config map[string]any) string {
|
|
salt := config["salt"].(string)
|
|
charset := config["charset"].(string)
|
|
|
|
value = strings.TrimPrefix(value, salt)
|
|
value = strings.TrimPrefix(value, charset)
|
|
|
|
if caseRule, ok := config["case"].(string); ok {
|
|
if caseRule == "upper" {
|
|
value = strings.ToLower(value)
|
|
}
|
|
}
|
|
|
|
return value
|
|
}
|
|
|
|
// Function Purpose: Apply output casing rules.
|
|
func obrimCodecApplyCase(value string, rule string) string {
|
|
switch rule {
|
|
case "upper":
|
|
return strings.ToUpper(value)
|
|
default:
|
|
return strings.ToLower(value)
|
|
}
|
|
}
|
|
|
|
// Function Purpose: Convert supported length types into integer.
|
|
func obrimCodecLength(value any) int {
|
|
switch v := value.(type) {
|
|
case int:
|
|
return v
|
|
case int8:
|
|
return int(v)
|
|
case int16:
|
|
return int(v)
|
|
case int32:
|
|
return int(v)
|
|
case int64:
|
|
return int(v)
|
|
default:
|
|
return 0
|
|
}
|
|
}
|