/* |-------------------------------------------------------------------------- | Metadata |-------------------------------------------------------------------------- | | Name: | - Codec | | Purpose: | - Provide reusable encoding and decoding capabilities for framework | and software services. | | Guideline: | - Use ObrimCodec() as the public entry point for all codec operations. | - Always provide a valid operation type and configuration map. | - Use supported formats only: base8, base10, base16, base32, | base64, and sqids. | - Validate all inputs before processing encoding or decoding requests. | - Use charset and salt values consistently between encode and decode | operations. | - Use case configuration for non-sqids formats. | - Use length configuration for sqids format. | - Treat empty sqids inputs and outputs as operation failures. | - Consume responses through the standardized status, code, and | payload structure. | | Example: | - ObrimCodec("encode", config) | - ObrimCodec("decode", config) | - ObrimCodec("encode", map[string]any{ | "format": "base64", | "data": "example", | "charset": "", | "salt": "", | "case": "lower", | }) | - ObrimCodec("decode", map[string]any{ | "format": "sqids", | "data": "abc123", | "charset": "abcdefghijklmnopqrstuvwxyz", | "salt": "", | "length": 8, | }) | |-------------------------------------------------------------------------- */ /* |-------------------------------------------------------------------------- | Credit |-------------------------------------------------------------------------- | | Contributor: | - Rajon Ahmed | - Blockonite | |-------------------------------------------------------------------------- */ package codec import ( "encoding/base32" "encoding/base64" "encoding/hex" "errors" "fmt" "math/big" "strconv" "strings" "github.com/sqids/sqids-go" ) const ( obrimCodecTypeEncode = "encode" obrimCodecTypeDecode = "decode" obrimCodecFormatBase8 = "base8" obrimCodecFormatBase10 = "base10" obrimCodecFormatBase16 = "base16" obrimCodecFormatBase32 = "base32" obrimCodecFormatBase64 = "base64" obrimCodecFormatSqids = "sqids" obrimCodecCaseLower = "lower" obrimCodecCaseUpper = "upper" ObrimCodecCodeSuccessEncode = "SUCCESS_ENCODE" ObrimCodecCodeSuccessDecode = "SUCCESS_DECODE" ObrimCodecCodeFailedValidation = "FAILED_VALIDATION" ObrimCodecCodeFailedUnsupportedType = "FAILED_UNSUPPORTED_TYPE" ObrimCodecCodeFailedUnsupportedFormat = "FAILED_UNSUPPORTED_FORMAT" ObrimCodecCodeFailedMissingConfiguration = "FAILED_MISSING_CONFIGURATION" ObrimCodecCodeFailedInvalidConfiguration = "FAILED_INVALID_CONFIGURATION" ObrimCodecCodeFailedOperation = "FAILED_OPERATION" ) // ObrimCodecResponse defines the standardized utility response. type ObrimCodecResponse struct { Status bool `json:"status"` Code string `json:"code"` Payload map[string]any `json:"payload"` } // obrimCodecConfiguration stores validated codec configuration. type obrimCodecConfiguration struct { Format string Data any Charset string Salt string Case string Length int } // ObrimCodec validates configuration, dispatches codec operations, and returns structured results. func ObrimCodec(codecType string, config map[string]any) map[string]any { validatedConfig, code := obrimCodecValidation(codecType, config) if code != "" { return obrimCodecBuildResponse(false, code, nil) } value, err := obrimCodecDispatch(codecType, validatedConfig) if err != nil { return obrimCodecBuildResponse( false, ObrimCodecCodeFailedOperation, nil, ) } var successCode string switch codecType { case obrimCodecTypeEncode: successCode = ObrimCodecCodeSuccessEncode case obrimCodecTypeDecode: successCode = ObrimCodecCodeSuccessDecode } return obrimCodecBuildResponse( true, successCode, map[string]any{ "operation": codecType, "format": validatedConfig.Format, "value": value, }, ) } // obrimCodecValidation validates required parameters, supported operations, formats, and parameter types. func obrimCodecValidation( codecType string, config map[string]any, ) (*obrimCodecConfiguration, string) { if config == nil { return nil, ObrimCodecCodeFailedMissingConfiguration } switch codecType { case obrimCodecTypeEncode: case obrimCodecTypeDecode: default: return nil, ObrimCodecCodeFailedUnsupportedType } formatValue, exists := config["format"] if !exists { return nil, ObrimCodecCodeFailedMissingConfiguration } format, ok := formatValue.(string) if !ok || strings.TrimSpace(format) == "" { return nil, ObrimCodecCodeFailedInvalidConfiguration } switch format { case obrimCodecFormatBase8: case obrimCodecFormatBase10: case obrimCodecFormatBase16: case obrimCodecFormatBase32: case obrimCodecFormatBase64: case obrimCodecFormatSqids: default: return nil, ObrimCodecCodeFailedUnsupportedFormat } dataValue, exists := config["data"] if !exists { return nil, ObrimCodecCodeFailedMissingConfiguration } switch dataValue.(type) { case string: case []byte: default: return nil, ObrimCodecCodeFailedInvalidConfiguration } charsetValue, exists := config["charset"] if !exists { return nil, ObrimCodecCodeFailedMissingConfiguration } charset, ok := charsetValue.(string) if !ok { return nil, ObrimCodecCodeFailedInvalidConfiguration } saltValue, exists := config["salt"] if !exists { return nil, ObrimCodecCodeFailedMissingConfiguration } salt, ok := saltValue.(string) if !ok { return nil, ObrimCodecCodeFailedInvalidConfiguration } var validatedConfig = &obrimCodecConfiguration{ Format: format, Data: dataValue, Charset: charset, Salt: salt, } if format == obrimCodecFormatSqids { lengthValue, exists := config["length"] if !exists { return nil, ObrimCodecCodeFailedMissingConfiguration } switch value := lengthValue.(type) { case int: validatedConfig.Length = value case int8: validatedConfig.Length = int(value) case int16: validatedConfig.Length = int(value) case int32: validatedConfig.Length = int(value) case int64: validatedConfig.Length = int(value) case float64: validatedConfig.Length = int(value) default: return nil, ObrimCodecCodeFailedInvalidConfiguration } if validatedConfig.Length < 0 { return nil, ObrimCodecCodeFailedInvalidConfiguration } } else { caseValue, exists := config["case"] if !exists { return nil, ObrimCodecCodeFailedMissingConfiguration } caseRule, ok := caseValue.(string) if !ok { return nil, ObrimCodecCodeFailedInvalidConfiguration } switch caseRule { case obrimCodecCaseLower: case obrimCodecCaseUpper: default: return nil, ObrimCodecCodeFailedInvalidConfiguration } validatedConfig.Case = caseRule } return validatedConfig, "" } // obrimCodecDispatch routes validated requests to the appropriate codec implementation. func obrimCodecDispatch( codecType string, config *obrimCodecConfiguration, ) (string, error) { switch codecType { case obrimCodecTypeEncode: switch config.Format { case obrimCodecFormatBase8: return obrimCodecEncodeBase8(config) case obrimCodecFormatBase10: return obrimCodecEncodeBase10(config) case obrimCodecFormatBase16: return obrimCodecEncodeBase16(config) case obrimCodecFormatBase32: return obrimCodecEncodeBase32(config) case obrimCodecFormatBase64: return obrimCodecEncodeBase64(config) case obrimCodecFormatSqids: return obrimCodecEncodeSqids(config) } case obrimCodecTypeDecode: switch config.Format { case obrimCodecFormatBase8: return obrimCodecDecodeBase8(config) case obrimCodecFormatBase10: return obrimCodecDecodeBase10(config) case obrimCodecFormatBase16: return obrimCodecDecodeBase16(config) case obrimCodecFormatBase32: return obrimCodecDecodeBase32(config) case obrimCodecFormatBase64: return obrimCodecDecodeBase64(config) case obrimCodecFormatSqids: return obrimCodecDecodeSqids(config) } } return "", obrimCodecError("unsupported dispatch") } // obrimCodecBuildResponse builds a standard response. func obrimCodecBuildResponse( status bool, code string, payload map[string]any, ) map[string]any { return map[string]any{ "status": status, "code": code, "payload": payload, } } // obrimCodecNormalizeInput converts supported input types into string form. func obrimCodecNormalizeInput(data any) string { switch value := data.(type) { case string: return value case []byte: return string(value) default: return "" } } // obrimCodecApplyTransform applies charset and salt transformations. func obrimCodecApplyTransform( value string, charset string, salt string, ) string { return charset + salt + value } // obrimCodecReverseTransform reverses charset and salt transformations. func obrimCodecReverseTransform( value string, charset string, salt string, ) string { var prefix = charset + salt return strings.TrimPrefix(value, prefix) } // obrimCodecApplyCase applies output casing. func obrimCodecApplyCase( value string, caseRule string, ) string { switch caseRule { case obrimCodecCaseUpper: return strings.ToUpper(value) default: return strings.ToLower(value) } } // obrimCodecEncodeBase8 encodes data using Base8 encoding. func obrimCodecEncodeBase8( config *obrimCodecConfiguration, ) (string, error) { var input = obrimCodecApplyTransform( obrimCodecNormalizeInput(config.Data), config.Charset, config.Salt, ) var builder strings.Builder for _, value := range []byte(input) { builder.WriteString(fmt.Sprintf("%03o", value)) } return obrimCodecApplyCase( builder.String(), config.Case, ), nil } // obrimCodecDecodeBase8 decodes Base8 encoded data. func obrimCodecDecodeBase8( config *obrimCodecConfiguration, ) (string, error) { var input = strings.ToLower( obrimCodecNormalizeInput(config.Data), ) if len(input)%3 != 0 { return "", obrimCodecError("invalid base8 data") } var output []byte for index := 0; index < len(input); index += 3 { value, err := strconv.ParseUint( input[index:index+3], 8, 8, ) if err != nil { return "", err } output = append(output, byte(value)) } return obrimCodecReverseTransform( string(output), config.Charset, config.Salt, ), nil } // obrimCodecEncodeBase10 encodes data using Base10 encoding. func obrimCodecEncodeBase10( config *obrimCodecConfiguration, ) (string, error) { var input = obrimCodecApplyTransform( obrimCodecNormalizeInput(config.Data), config.Charset, config.Salt, ) var value = new(big.Int).SetBytes([]byte(input)) return obrimCodecApplyCase( value.Text(10), config.Case, ), nil } // obrimCodecDecodeBase10 decodes Base10 encoded data. func obrimCodecDecodeBase10( config *obrimCodecConfiguration, ) (string, error) { var value = new(big.Int) if _, ok := value.SetString( obrimCodecNormalizeInput(config.Data), 10, ); !ok { return "", obrimCodecError("invalid base10 data") } return obrimCodecReverseTransform( string(value.Bytes()), config.Charset, config.Salt, ), nil } // obrimCodecEncodeBase16 encodes data using Base16 encoding. func obrimCodecEncodeBase16( config *obrimCodecConfiguration, ) (string, error) { var input = obrimCodecApplyTransform( obrimCodecNormalizeInput(config.Data), config.Charset, config.Salt, ) var output = hex.EncodeToString([]byte(input)) return obrimCodecApplyCase( output, config.Case, ), nil } // obrimCodecDecodeBase16 decodes Base16 encoded data. func obrimCodecDecodeBase16( config *obrimCodecConfiguration, ) (string, error) { var output, err = hex.DecodeString( obrimCodecNormalizeInput(config.Data), ) if err != nil { return "", err } return obrimCodecReverseTransform( string(output), config.Charset, config.Salt, ), nil } // obrimCodecEncodeBase32 encodes data using Base32 encoding. func obrimCodecEncodeBase32( config *obrimCodecConfiguration, ) (string, error) { var input = obrimCodecApplyTransform( obrimCodecNormalizeInput(config.Data), config.Charset, config.Salt, ) var output = base32.StdEncoding.EncodeToString( []byte(input), ) return obrimCodecApplyCase( output, config.Case, ), nil } // obrimCodecDecodeBase32 decodes Base32 encoded data. func obrimCodecDecodeBase32( config *obrimCodecConfiguration, ) (string, error) { var output, err = base32.StdEncoding.DecodeString( strings.ToUpper( obrimCodecNormalizeInput(config.Data), ), ) if err != nil { return "", err } return obrimCodecReverseTransform( string(output), config.Charset, config.Salt, ), nil } // obrimCodecEncodeBase64 encodes data using Base64 encoding. func obrimCodecEncodeBase64( config *obrimCodecConfiguration, ) (string, error) { var input = obrimCodecApplyTransform( obrimCodecNormalizeInput(config.Data), config.Charset, config.Salt, ) var output = base64.StdEncoding.EncodeToString( []byte(input), ) return obrimCodecApplyCase( output, config.Case, ), nil } // obrimCodecDecodeBase64 decodes Base64 encoded data. func obrimCodecDecodeBase64( config *obrimCodecConfiguration, ) (string, error) { var output, err = base64.StdEncoding.DecodeString( obrimCodecNormalizeInput(config.Data), ) if err != nil { return "", err } return obrimCodecReverseTransform( string(output), config.Charset, config.Salt, ), nil } // obrimCodecEncodeSqids encodes data using Sqids encoding with minimum length enforcement. func obrimCodecEncodeSqids( config *obrimCodecConfiguration, ) (string, error) { var input = obrimCodecApplyTransform( obrimCodecNormalizeInput(config.Data), config.Charset, config.Salt, ) var numbers = make([]uint64, 0, len(input)) for _, value := range []byte(input) { numbers = append(numbers, uint64(value)) } if len(numbers) == 0 { return "", obrimCodecError("empty sqids input") } var sqidsInstance, err = sqids.New( sqids.Options{ Alphabet: config.Charset, MinLength: uint8(config.Length), }, ) if err != nil { return "", err } return sqidsInstance.Encode(numbers) } // obrimCodecDecodeSqids decodes Sqids encoded data using deterministic length validation. func obrimCodecDecodeSqids( config *obrimCodecConfiguration, ) (string, error) { var sqidsInstance, err = sqids.New( sqids.Options{ Alphabet: config.Charset, MinLength: uint8(config.Length), }, ) if err != nil { return "", err } var numbers = sqidsInstance.Decode( obrimCodecNormalizeInput(config.Data), ) if len(numbers) == 0 { return "", obrimCodecError( "invalid or empty sqids data", ) } var output = make([]byte, 0, len(numbers)) for _, value := range numbers { if value > 255 { return "", obrimCodecError( "decoded sqids value exceeds byte range", ) } output = append(output, byte(value)) } return obrimCodecReverseTransform( string(output), config.Charset, config.Salt, ), nil } // obrimCodecError formats internal errors. func obrimCodecError(message string) error { return errors.New(message) }