Base32

Share your advanced PureBasic knowledge/code with the community.
infratec
Always Here
Always Here
Posts: 7575
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Base32

Post by infratec »

SInce it was requested:

Code: Select all

;
; Base32
;
; RFC 3548 / RFC 4648
;
; https://www.purebasic.fr/english/viewtopic.php?t=84906
;

CompilerIf #PB_Compiler_IsMainFile
  EnableExplicit
CompilerEndIf


DataSection
  Base32EncoderTable:
  Data.a 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '2', '3', '4', '5', '6', '7'
  
  Base32DecoderTable:
  Data.b -1, -1, 26, 27, 28, 29, 30, 31, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 , 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25
  
EndDataSection


Procedure.i Base32EncoderBuffer(*Input, InputSize.i, *Output, OutputSize.i, Flags.i=0)
  
  Protected Index.i, i.i, *OutputAscii.Ascii, *TableAscii.Ascii, c.i, n.i
  Protected Dim Block.a(4)
  
  
  *OutputAscii = *Output
  
  While i < InputSize
    
    If i + 5 < InputSize
      CopyMemory(*Input + i, @Block(0), 5)
      c = 8
    Else
      FillMemory(@Block(0), 5)
      CopyMemory(*Input + i, @Block(0), InputSize - i)
      Select 5 - (InputSize - i)
        Case 1 : c = 7
        Case 2 : c = 5
        Case 3 : c = 4
        Case 4 : c = 2
      EndSelect
    EndIf
    
    n = 1
    While n <= c
      Select n
        Case 1 : Index = Block(0) >> 3
        Case 2 : Index = (Block(0) & %111) << 2 | Block(1) >> 6
        Case 3 : Index = (Block(1) & %111110) >> 1
        Case 4 : Index = (Block(1) & %1) << 4 | Block(2) >> 4
        Case 5 : Index = (Block(2) & %1111) << 1 | Block(3) >> 7
        Case 6 : Index = (Block(3) & %1111100) >> 2
        Case 7 : Index = (Block(3) & %11) << 3 | Block(4) >> 5
        Case 8 : Index = (Block(4) & %11111)
      EndSelect
      
      *TableAscii = ?Base32EncoderTable + Index
      *OutputAscii\a = *TableAscii\a
      *OutputAscii + 1
      n + 1
    Wend
    
    i + 5
  Wend
  
  If (Flags & #PB_Cipher_NoPadding) = 0
    For i = 7 To c Step -1
      *OutputAscii\a = '='
      *OutputAscii + 1
    Next i
  EndIf
  
  ProcedureReturn *OutputAscii - *Output
  
EndProcedure




Procedure.s Base32Encoder(*Input, InputSize.i, Flags.i=0)
  
  Protected Base32$, *OutBuffer, Length.i
  
  
  *OutBuffer = AllocateMemory(InputSize * Int(Round(8 / 5, #PB_Round_Up)))
  If *OutBuffer
    Length = Base32EncoderBuffer(*Input, InputSize, *OutBuffer, MemorySize(*OutBuffer), Flags)
    If Length > 0
      Base32$ = PeekS(*OutBuffer, Length, #PB_Ascii)
    EndIf
    FreeMemory(*OutBuffer)
  EndIf
  
  ProcedureReturn Base32$
  
EndProcedure






Procedure.i Base32DecoderBuffer(*Input, InputSize.i, *Output, OutputSize.i)
  
  Protected *InputAscii.Ascii, *OutputAscii.Ascii, i.i, Byte.a, *TableAscii.Ascii, n.i, c.i
  Protected Dim Block.a(4)
  
  
  *InputAscii = *Input
  *OutputAscii = *Output
  
  While i < InputSize
    
    FillMemory(@Block(0), 4)
    
    n = 1
    While n <= 8
      If *InputAscii\a = '='
        Break
      Else
        
        *TableAscii = ?Base32DecoderTable + *InputAscii\a - '0'
        
        ;Debug Chr(*InputAscii\a) + " " + Str(*InputAscii\a - '0') + " " +  Str(*TableAscii\a) + " 0x" + Hex(*TableAscii\a)
        
        Select n
          Case 1 : Block(0) = *TableAscii\a << 3
          Case 2 : Block(0) | (*TableAscii\a >> 2) : c = 1 : Block(1) = (*TableAscii\a & %11) << 6
          Case 3 : Block(1) | (*TableAscii\a << 1)
          Case 4 : Block(1) | (*TableAscii\a >> 4) : c = 2 : Block(2) = (*TableAscii\a & %1111) << 4
          Case 5 : Block(2) | (*TableAscii\a >> 1) : c = 3 : Block(3) = (*TableAscii\a & %1) << 7
          Case 6 : Block(3) | (*TableAscii\a << 2)
          Case 7 : Block(3) | (*TableAscii\a >> 3) : c = 4 : Block(4) = (*TableAscii\a & %111) << 5
          Case 8 : Block(4) | *TableAscii\a : c = 5
        EndSelect
        
        *InputAscii + 1
      EndIf
      n + 1
    Wend
    
    CopyMemory(@Block(0), *OutputAscii, c)
    *OutputAscii + c
    
    i + 8
  Wend
  
  ProcedureReturn *OutputAscii - *Output
  
EndProcedure



Procedure.i Base32Decoder(Input$, *Output, OutputSize.i)
  
  Protected Length.i, *Input
  
  
  *Input = Ascii(Input$)
  If *Input
    Length = Base32DecoderBuffer(*Input, MemorySize(*Input) - 1, *Output, OutputSize)
    FreeMemory(*Input)
  EndIf
  
  ProcedureReturn Length
  
EndProcedure





CompilerIf #PB_Compiler_IsMainFile
  
  Define Example$, *Example, *Decoded, *Encoded, Length.i, Encoded$
  
  Example$ = "This is a test string!"
  *Example = UTF8(Example$)
  ;*Example = AllocateMemory(StringByteLength(Example$) + 2)
  ;PokeS(*Example, Example$)
  
  
  Encoded$ = Base32Encoder(*Example, MemorySize(*Example) - 1)
  Debug Encoded$
  
  
  *Decoded = AllocateMemory(1024)
  *Encoded = AllocateMemory(1024)
  
  Length = Base32EncoderBuffer(*Example, MemorySize(*Example) - 1, *Encoded, MemorySize(*Encoded))
  Debug PeekS(*Encoded, Length, #PB_Ascii)
  
  Length = Base32DecoderBuffer(*Encoded, Length, *Decoded, MemorySize(*Decoded))
  Debug PeekS(*Decoded, Length, #PB_UTF8|#PB_ByteLength)
  ;Debug PeekS(*Decoded, Length / 2)
  
  FillMemory(*Decoded, MemorySize(*Decoded))
  Length = Base32Decoder(Encoded$, *Decoded, MemorySize(*Decoded))
  Debug PeekS(*Decoded, Length, #PB_UTF8|#PB_ByteLength)
  
  
CompilerEndIf
But there are no security checks for to small output buffers :!:
Quin
Addict
Addict
Posts: 1122
Joined: Thu Mar 31, 2022 7:03 pm
Location: Colorado, United States
Contact:

Re: Base32

Post by Quin »

Looks great and works perfectly on my machine. Thanks!
Post Reply