'########################################################################
' FUNCTION: W_JSON_EscapeString (Pointer-Optimized)
'------------------------------------------------------------------------
' Purpose: Escapes a WSTRING for JSON compliance.
' - Uses BYREF U01 for fastest read speed.
' - Uses Pointer arithmetic for high-speed filling.
' Parameters:
' U01 (WSTRING) : Input String (BYREF)
' U02 (LONG) : Escape Slash Flag (1=Yes, 0=No) (BYVAL)
' Returns:
' WSTRING : Escaped JSON String
'########################################################################
' FUNCTION: W_JSON_EscapeString (FINAL LOGIC FIX)
'------------------------------------------------------------------------
' Purpose: Ensures perfect JSON compliance and fixes the Test 1 failure.
'########################################################################
FUNCTION W_JSON_EscapeString(BYREF U01 AS WSTRING,OPT BYVAL U02 AS LONG) COMMON AS WSTRING
' --- 1. Variable Declarations ---
LOCAL pIn AS WORD PTR
LOCAL pEnd AS WORD PTR
LOCAL pOut AS WORD PTR
LOCAL S01 AS WSTRING ' Output Buffer
LOCAL T01 AS LONG ' Input Length
LOCAL T02 AS LONG ' Output Length
LOCAL T03 AS WORD ' Current Char
LOCAL S02 AS STRING ' Hex Helper
' --- 2. Input Validation ---
T01 = LEN(U01)
IF T01 = 0 THEN
S01 = ""
GOTO EndFunction
END IF
' --- 3. PASS 1: Calculate Required Output Length ---
pIn = STRPTR(U01)
pEnd = pIn + (T01 * 2)
T02 = 0
DO WHILE pIn < pEnd
T03 = @pIn
SELECT CASE T03
CASE 34, 92
T02 = T02 + 2
CASE 47
IF U02 THEN T02 = T02 + 2 ELSE T02 = T02 + 1
CASE 8, 9, 10, 12, 13
T02 = T02 + 2
CASE 32 TO 126
T02 = T02 + 1
CASE ELSE
T02 = T02 + 6
END SELECT
INCR pIn
LOOP
' --- 4. Allocation ---
S01 = SPACE$(T02)
pIn = STRPTR(U01)
pOut = STRPTR(S01)
' --- 5. PASS 2: Build Escaped String ---
DO WHILE pIn < pEnd
T03 = @pIn
SELECT CASE AS LONG T03
CASE 34, 92 ' " and \
@pOut = 92 : INCR pOut ' Write \
@pOut = T03: INCR pOut ' Write " or \
CASE 47 ' /
IF U02 THEN
@pOut = 92 : INCR pOut ' Write \
END IF
@pOut = 47 : INCR pOut ' Write /
CASE 8 : @pOut = 92 : INCR pOut : @pOut = 98 : INCR pOut ' \b
CASE 9 : @pOut = 92 : INCR pOut : @pOut = 116 : INCR pOut ' \t
CASE 10 : @pOut = 92 : INCR pOut : @pOut = 110 : INCR pOut ' \n
CASE 12 : @pOut = 92 : INCR pOut : @pOut = 102 : INCR pOut ' \f
CASE 13 : @pOut = 92 : INCR pOut : @pOut = 114 : INCR pOut ' \r
CASE 32 TO 126 ' Safe ASCII
@pOut = T03 : INCR pOut
CASE ELSE ' \uXXXX format (Covers BMP Unicode and Controls 0-7, 11, 14-31)
@pOut = 92 : INCR pOut ' \
@pOut = 117 : INCR pOut ' u
S02 = HEX$(T03, 4)
@pOut = ASC(S02, 1) : INCR pOut
@pOut = ASC(S02, 2) : INCR pOut
@pOut = ASC(S02, 3) : INCR pOut
@pOut = ASC(S02, 4) : INCR pOut
END SELECT
INCR pIn
LOOP
EndFunction:
FUNCTION = S01
END FUNCTION
'########################################################################
' FUNCTION: HTP_Encode_Base64
'------------------------------------------------------------------------
' Purpose: Encodes raw binary data into Base64 (RFC 4648).
' Uses High-Speed Inline ASM.
' Parameters:
' U01 (InBuf) - Input String (Binary Data)
' U02 (nGroups) - Line break interval (0 = None, standard for APIs)
' U03 (OutBuf) - Output String (Base64) - Will be resized automatically
' Returns:
' LONG - Actual length of the encoded string.
'########################################################################
FUNCTION HTP_Encode_Base64(BYREF U01 AS STRING, BYVAL U02 AS LONG, BYREF U03 AS STRING) COMMON AS LONG
' Local Variables
LOCAL E01 AS LONG ' Input Length
LOCAL E02 AS LONG ' Output Buffer Size
LOCAL U04 AS DWORD ' pIn
LOCAL U05 AS DWORD ' pOut
LOCAL U06 AS DWORD ' pTbl (Lookup Table)
LOCAL U07 AS DWORD ' pEnd (Input End)
LOCAL T01 AS LONG ' Temp
LOCAL T_LineCtr AS LONG ' Line Counter for Groups
E01 = LEN(U01)
IF E01 = 0 THEN U03 = "" : EXIT FUNCTION
' --- 1. Calculate Output Size ---
' Base size: (Len + 2) / 3 * 4
E02 = ((E01 + 2) \ 3) * 4
' Add Line Breaks if nGroups > 0
IF U02 > 0 THEN
' Add 2 bytes (CRLF) for every nGroups * 4 chars
E02 = E02 + ((E02 \ (U02 * 4)) * 2) + 2 ' +2 Safety margin
END IF
' Pre-allocate Output
U03 = SPACE$(E02)
' --- 2. Initialize Lookup Table ---
STATIC S_Tbl AS STRING
IF LEN(S_Tbl) = 0 THEN
S_Tbl = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
END IF
' --- 3. Setup Pointers ---
U04 = STRPTR(U01) ' Source
U05 = STRPTR(U03) ' Dest
U06 = STRPTR(S_Tbl) ' Table
U07 = U04 + E01 ' End of Source
T_LineCtr = U02 ' Init Line Counter
' --- 4. ASM Encoding Loop ---
! PUSH ESI
! PUSH EDI
! PUSH EBX
! MOV ESI, U04 ; ESI = Input
! MOV EDI, U05 ; EDI = Output
! MOV EBX, U06 ; EBX = Table
! MOV ECX, U07 ; ECX = End of Input
EncLoop:
! CMP ESI, ECX ; Check if we reached end
! JAE DoneASM
' Check if we have at least 3 bytes remaining
! MOV EAX, ECX
! SUB EAX, ESI
! CMP EAX, 3
! JL PartialBlock
' --- Process Full 3-Byte Block ---
' Read 3 bytes: [ESI], [ESI+1], [ESI+2]
' Produce 4 chars
' Byte 1 (Src[0]) -> Out[0] (Top 6 bits)
! MOV AL, [ESI]
! SHR AL, 2
! XLAT ; Translate
! MOV [EDI], AL
! INC EDI
' Byte 1+2 -> Out[1] (Bot 2 bits of Src[0] + Top 4 bits of Src[1])
! MOV AL, [ESI]
! AND AL, 3 ; Keep last 2 bits
! SHL AL, 4 ; Move to top
! MOV AH, [ESI+1] ; Load Byte 2
! SHR AH, 4 ; Get top 4 bits
! OR AL, AH ; Combine
! XLAT
! MOV [EDI], AL
! INC EDI
' Byte 2+3 -> Out[2] (Bot 4 bits of Src[1] + Top 2 bits of Src[2])
! MOV AL, [ESI+1]
! AND AL, 15 ; Keep last 4 bits
! SHL AL, 2 ; Move to top
! MOV AH, [ESI+2] ; Load Byte 3
! SHR AH, 6 ; Get top 2 bits
! OR AL, AH ; Combine
! XLAT
! MOV [EDI], AL
! INC EDI
' Byte 3 -> Out[3] (Bot 6 bits of Src[2])
! MOV AL, [ESI+2]
! AND AL, 63 ; Keep last 6 bits
! XLAT
! MOV [EDI], AL
! INC EDI
! ADD ESI, 3 ; Advance Input
' --- Handle Line Breaks (Only if nGroups > 0) ---
' PowerBasic check: accessing U02 variable
! MOV EAX, U02
! CMP EAX, 0
! JE EncLoop ; If nGroups == 0, skip
! DEC T_LineCtr ; Decrement counter
! JNZ EncLoop ; If not zero, continue
! MOV EAX, U02 ; Reset Counter
! MOV T_LineCtr, EAX
' Insert CRLF
! MOV BYTE PTR [EDI], 13
! INC EDI
! MOV BYTE PTR [EDI], 10
! INC EDI
! JMP EncLoop
PartialBlock:
' Handle remaining 1 or 2 bytes
! CMP EAX, 1
! JE Rem1Byte
' --- Remaining 2 Bytes ---
' Src[0], Src[1] -> Out[0], Out[1], Out[2], "="
' Out[0]
! MOV AL, [ESI]
! SHR AL, 2
! XLAT
! MOV [EDI], AL
! INC EDI
' Out[1]
! MOV AL, [ESI]
! AND AL, 3
! SHL AL, 4
! MOV AH, [ESI+1]
! SHR AH, 4
! OR AL, AH
! XLAT
! MOV [EDI], AL
! INC EDI
' Out[2] (Bot 4 bits of Src[1] shifted left 2)
! MOV AL, [ESI+1]
! AND AL, 15
! SHL AL, 2
! XLAT
! MOV [EDI], AL
! INC EDI
' Out[3] Padding
! MOV BYTE PTR [EDI], 61 ; '='
! INC EDI
! ADD ESI, 2
! JMP DoneASM
Rem1Byte:
' --- Remaining 1 Byte ---
' Src[0] -> Out[0], Out[1], "=", "="
' Out[0]
! MOV AL, [ESI]
! SHR AL, 2
! XLAT
! MOV [EDI], AL
! INC EDI
' Out[1] (Bot 2 bits of Src[0] shifted left 4)
! MOV AL, [ESI]
! AND AL, 3
! SHL AL, 4
! XLAT
! MOV [EDI], AL
! INC EDI
' Padding
! MOV BYTE PTR [EDI], 61 ; '='
! INC EDI
! MOV BYTE PTR [EDI], 61 ; '='
! INC EDI
! INC ESI
DoneASM:
! MOV U05, EDI ; Save final pointer
! POP EBX
! POP EDI
! POP ESI
' --- 5. Final Resize ---
T01 = U05 - STRPTR(U03)
U03 = LEFT$(U03, T01)
FUNCTION = T01
END FUNCTION
'########################################################################
' FUNCTION: HTP_DecodeBase64
'------------------------------------------------------------------------
' Purpose: Decodes a Base64-encoded string (RFC 4648).
' Uses high-performance pointer logic and static lookup table
' for maximum speed, and handles MIME headers and whitespace.
' Parameters:
' U01 (b64In AS STRING): The Base64 string to decode. May include
' optional MIME header (e.g., "data:image/..").
' Returns:
' AS STRING: The decoded binary string. Empty string on error or empty input.
'########################################################################
FUNCTION HTP_DecodeBase64(BYVAL U01 AS STRING) COMMON AS STRING
REGISTER T01 AS LONG, T02 AS LONG ' i, k (General purpose LONGs)
LOCAL E01 AS LONG ' L (Input Length)
LOCAL T03 AS LONG, T04 AS LONG, T05 AS LONG, T06 AS LONG ' b1, b2, b3, b4 (Decoded 6-bit values)
LOCAL D01 AS LONG, D02 AS LONG ' t1, t2 (Temporary shift LONGs)
LOCAL S01 AS STRING ' sOut (Output buffer string)
LOCAL U02 AS BYTE PTR ' pIn (Input string pointer)
LOCAL U03 AS BYTE PTR ' pOut (Output string pointer)
LOCAL U04 AS BYTE PTR ' pEnd (End of input string pointer)
' --- 1. Strip MIME Header if present (Bulletproofing) ---
IF LEFT$(U01, 5) = "data:" THEN
T01 = INSTR(U01, "base64,")
IF T01 > 0 THEN U01 = MID$(U01, T01 + 7)
END IF
E01 = LEN(U01)
IF E01 = 0 THEN EXIT FUNCTION
' --- 2. Initialize Reverse Lookup Table (STATIC = Fast) ---
STATIC TblE() AS BYTE ' E01 for 'Extra' datatypes
IF UBOUND(TblE) = -1 THEN
REDIM TblE(255)
FOR T01 = 0 TO 255 : TblE(T01) = 255 : NEXT T01 ' Initialize to 255 (Invalid)
FOR T01 = 65 TO 90 : TblE(T01) = T01 - 65 : NEXT T01 ' A(65)-Z(90) -> 0-25
FOR T01 = 97 TO 122: TblE(T01) = T01 - 71 : NEXT T01 ' a(97)-z(122) -> 26-51
FOR T01 = 48 TO 57 : TblE(T01) = T01 + 4 : NEXT T01 ' 0(48)-9(57) -> 52-61
TblE(43) = 62 ' + (43)
TblE(47) = 63 ' / (47)
TblE(61) = 255 ' = (61) - Treat padding as invalid for lookup (we check separately)
END IF
' --- 3. Pre-allocate Output Buffer ---
' Maximum output size: 3 bytes per 4 input characters.
S01 = STRING$((E01 \ 4) * 3 + 3, 0)
U02 = STRPTR(U01)
U03 = STRPTR(S01)
U04 = U02 + E01
' --- 4. Fast Decode Loop ---
DO WHILE U02 < U04
' Skip non-Base64 chars (CR/LF/Space/Tabs - ASCII 1-32)
DO WHILE (U02 < U04) AND (@U02 <= 32) : INCR U02 : LOOP
IF U02 >= U04 THEN EXIT LOOP
' Fetch 4 bytes and convert to 6-bit values
' Use T01 for current byte value, then TblE for 6-bit value
T03 = TblE(@U02) : IF @U02 = 61 THEN T03 = 255 ' b1: Must not be padding (=)
INCR U02
T04 = TblE(@U02) : IF @U02 = 61 THEN T04 = 255 ' b2: Can be 6-bit value or 255
INCR U02
T05 = TblE(@U02) : IF @U02 = 61 THEN T05 = 255 ELSE IF T05 = 255 THEN EXIT LOOP ' b3: 6-bit value, or 255 (Padding/Invalid)
INCR U02
T06 = TblE(@U02) : IF @U02 = 61 THEN T06 = 255 ELSE IF T06 = 255 THEN EXIT LOOP ' b4: 6-bit value, or 255 (Padding/Invalid)
INCR U02
' Check for any invalid character that slipped past the skip loop and was not padding
IF T03 = 255 OR T04 = 255 THEN EXIT LOOP
' Decode 3 bytes:
' Byte 1: b1(6) + b2(2)
D01 = T03 : SHIFT LEFT D01, 2
D02 = T04 : SHIFT RIGHT D02, 4
@U03 = D01 OR D02
INCR U03 ' <-- FIX: Increment the pointer
IF T05 = 255 THEN EXIT LOOP ' Padding found (e.g., XX==) - Stop here
' Byte 2: b2(4) + b3(4)
D01 = T04 AND 15 : SHIFT LEFT D01, 4
D02 = T05 : SHIFT RIGHT D02, 2
@U03 = D01 OR D02
INCR U03 ' <-- FIX: Increment the pointer
IF T06 = 255 THEN EXIT LOOP ' Padding found (e.g., XXX=) - Stop here
' Byte 3: b3(2) + b4(6)
D01 = T05 AND 3 : SHIFT LEFT D01, 6
@U03 = D01 OR T06
INCR U03 ' <-- FIX: Increment the pointer
LOOP
' --- 5. Resize to Actual Length ---
T02 = U03 - STRPTR(S01) ' Calculate actual length by pointer difference
FUNCTION = LEFT$(S01, T02)
END FUNCTION
Page created in 0.125 seconds with 11 queries.