Answered Variable length encoding - 'C' code converted into ABL

Cecil

19+ years progress programming and still learning.
I need to be able to convert some C code into ABL details below.


The Remaining Length is the number of bytes remaining within the current packet, including data in the variable header and the payload. The Remaining Length does not include the bytes used to encode the Remaining Length.



The Remaining Length is encoded using a variable length encoding scheme which uses a single byte for values up to 127. Larger values are handled as follows. The least significant seven bits of each byte encode the data, and the most significant bit is used to indicate that there are following bytes in the representation. Thus each byte encodes 128 values and a "continuation bit". The maximum number of bytes in the Remaining Length field is four.



Non normative comment

For example, the number 64 decimal is encoded as a single byte, decimal value 64, hexadecimal 0x40. The number 321 decimal (= 65 + 2*128) is encoded as two bytes, least significant first. The first byte is 65+128 = 193. Note that the top bit is set to indicate at least one following byte. The second byte is 2.



Non normative comment

This allows applications to send Control Packets of size up to 268,435,455 (256 MB). The representation of this number on the wire is: 0xFF, 0xFF, 0xFF, 0x7F.

Table 2.4 shows the Remaining Length values represented by increasing numbers of bytes.



Table 2.4 Size of Remaining Length field​

Digits
From
To
1​
0 (0x00)127 (0x7F)
2​
128 (0x80, 0x01)16 383 (0xFF, 0x7F)
3​
16 384 (0x80, 0x80, 0x01)2 097 151 (0xFF, 0xFF, 0x7F)
4​
2 097 152 (0x80, 0x80, 0x80, 0x01)268 435 455 (0xFF, 0xFF, 0xFF, 0x7F)


Non normative comment

The algorithm for encoding a non negative integer (X) into the variable length encoding scheme is as follows:

do

encodedByte = X MOD 128

X = X DIV 128

// if there are more data to encode, set the top bit of this byte

if ( X > 0 )

encodedByte = encodedByte OR 128

endif

'output' encodedByte

while ( X > 0 )



Where MOD is the modulo operator (% in C), DIV is integer division (/ in C), and OR is bit-wise or (| in C).



Non normative comment

The algorithm for decoding the Remaining Length field is as follows:



multiplier = 1

value = 0

do

encodedByte = 'next byte from stream'

value += (encodedByte AND 127) * multiplier

multiplier *= 128

if (multiplier > 128*128*128)

throw Error(Malformed Remaining Length)

while ((encodedByte AND 128) != 0)



where AND is the bit-wise and operator (& in C).



When this algorithm terminates, value contains the Remaining Length value.
 
Something like that?

Code:
DEFINE VARIABLE i            AS INTEGER   NO-UNDO.
DEFINE VARIABLE iByte        AS INTEGER   NO-UNDO.
DEFINE VARIABLE iEncodedByte AS INTEGER   EXTENT 4 INITIAL -1 NO-UNDO.
DEFINE VARIABLE iMultiplier  AS INTEGER   NO-UNDO.
DEFINE VARIABLE iValue       AS INTEGER   INITIAL 1728 NO-UNDO. /* value to encode */.

/* encode */

i = 1.

DO WHILE iValue > 0:
    iEncodedByte[i] = iValue MODULO 128.
    iValue = TRUNCATE(iValue / 128, 0).
    IF iValue > 0 THEN DO:
        PUT-BITS( iEncodedByte[i], 8, 1) = 1.
        i = i + 1.
    END.
END.

MESSAGE i "bytes" SKIP
    iEncodedByte[1] iEncodedByte[2] iEncodedByte[3] iEncodedByte[4] 
    VIEW-AS ALERT-BOX INFO BUTTONS OK.

/* decode */

i           = 0.
iValue      = 0.
iMultiplier = 1.

DO WHILE TRUE:
    i = i + 1.
    iByte = iEncodedByte[i].
    IF iByte < 0 THEN LEAVE.
    PUT-BITS(iByte, 8, 1) = 0.
    iValue = iValue + iByte * iMultiplier.
    iMultiplier = iMultiplier * 128.
    IF iMultiplier > 128 * 128 * 128 THEN DO:
        MESSAGE "Malformed Remaining Length"
            VIEW-AS ALERT-BOX ERROR BUTTONS OK.
        LEAVE.
    END.
    IF GET-BITS(iEncodedByte[i],8,1) = 0 THEN
        LEAVE.
END.

MESSAGE iValue
    VIEW-AS ALERT-BOX INFO BUTTONS OK.
 
Last edited:

Cecil

19+ years progress programming and still learning.
Big thank you

I was just struggling and I was overthinking the issue.

I have refactored your code into functions and made some minor tweeks.

Code:
// encode
Function variableLengthEncoding returns integer extent 4 (input iValue as integer,
                                                          output numBytesUsed as integer):

    define variable iByte        as integer   no-undo.
    define variable iEncodedByte as integer   extent 4 INITIAL -1 NO-UNDO.
    define variable iMultiplier  as integer   no-undo.
    
    // input value range check
    if iValue lt 0x00 or iValue gt 0xfffffff Then
    do:
        numBytesUsed = ?.
        return iEncodedByte.
    end.
        
    numBytesUsed = 1.   

    do while iValue gt 0:
        iEncodedByte[numBytesUsed] = iValue modulo 128.
        
        iValue = truncate(iValue / 128, 0).
        
        if iValue gt 0 then
        do:
            put-bits( iEncodedByte[numBytesUsed], 8, 1) = 1.
            numBytesUsed = numBytesUsed + 1.
        end.
    end.
    
    return iEncodedByte.

end function.

//decode
function variableLengthDecoding returns integer (input iEncodedByte as integer extent):

    define variable iBytePosition as integer no-undo.
    define variable iByte         as integer no-undo.
    define variable iValue        as integer no-undo.
    define variable iMultiplier   as integer no-undo.

    assign
        iBytePosition = 0
        iValue        = 0
        iMultiplier   = 1.

    DECODING-LOOP:
    do while true:
        iBytePosition = iBytePosition + 1.
        iByte = iEncodedByte[iBytePosition].
        
        if iByte lt 0 then
            leave.
            
        put-bits(iByte, 8, 1) = 0.
        
        iValue = iValue + iByte * iMultiplier.
        
        iMultiplier = iMultiplier * 128.
        
        if iMultiplier gt 0x200000 THEN       // dec 128 * 128 * 128 = hex 0x200000
        do:
            //MESSAGE "Malformed Remaining Length"  VIEW-AS ALERT-BOX ERROR BUTTONS OK.
            leave DECODING-LOOP.
        end.
        
        if get-bits(iEncodedByte[iBytePosition],8,1) eq 0 then
            leave DECODING-LOOP.
    END.
    
    return iValue.

end function .

define variable numBytesUsed    as integer  no-undo.
define variable iValue          as integer  no-undo.
define variable iEncodedByte    as integer  extent 4 no-undo.
define variable iCompareCheck   as integer  no-undo.
define variable dataStream      as memptr   no-undo.


iValue = random(1, 268435455).

iCompareCheck = iValue.
//iValue = -1000.

iEncodedByte = variableLengthEncoding(input  iValue,
                                      output numBytesUsed).
                                      
If numBytesUsed eq ? Then
Do:
    message substitute("Invalid value provided &1", quoter(iValue))
        view-as alert-box error title "Variable Length Encoding".
    return.   
End.

message "Encode Value: " iValue skip
        "Bytes Used:" numBytesUsed  skip
        "Encoded Bytes: " iEncodedByte[1] iEncodedByte[2] iEncodedByte[3] iEncodedByte[4] 
    view-as alert-box info buttons ok title "Variable Length Encoding".
    
    
    set-size(dataStream) = 0.
    set-size(dataStream) = 2 + numBytesUsed.  // first two btyes are reserved
    
    assign
        put-byte(dataStream, 3) = iEncodedByte[1] when iEncodedByte[1] ne -1
        put-byte(dataStream, 4) = iEncodedByte[2] when iEncodedByte[2] ne -1
        put-byte(dataStream, 5) = iEncodedByte[3] when iEncodedByte[3] ne -1
        put-byte(dataStream, 6) = iEncodedByte[4] when iEncodedByte[4] ne -1
        .
    
    iEncodedByte = -1.
    
    assign
        iEncodedByte[1] = get-byte(dataStream, 3) when get-size(dataStream) ge 3
        iEncodedByte[2] = get-byte(dataStream, 4) when get-size(dataStream) ge 4
        iEncodedByte[3] = get-byte(dataStream, 5) when get-size(dataStream) ge 5
        iEncodedByte[4] = get-byte(dataStream, 6) when get-size(dataStream) ge 6
        .
    
// decode the encoded btyes.   
iValue = variableLengthDecoding(input iEncodedByte).   

message "Encoded Bytes: " iEncodedByte[1] iEncodedByte[2] iEncodedByte[3] iEncodedByte[4] skip
        "Decoded Value: " iValue skip
        "Successful:" logical(iCompareCheck eq iValue)
    view-as alert-box info buttons ok title "Variable Length Decoding".
    
finally:
     set-size(dataStream) = 0.
end.
 
Top