Page 1 of 1

Base64 Helper functions

Posted: Mon Jun 27, 2016 1:47 pm
by kenmo
Base64 has come up a lot lately.
It sounds like Fred is going to make it easier to use in a future version (functions will return strings, so no allocations / ASCII peeks are needed).
But in the meantime, here are some helper functions to simplify Base64 usage.

Base64EncodeMemory(*Input, InputSize[, Flags]) - takes a pointer to some data and returns a Base64-formatted string
Base64DecodeToMemory(Input) - takes a Base64 string and returns a decoded memory block

Also, if you are encoding/decoding strings, these functions will auto-convert to/from UTF-8 so that Base64 results are the same in ASCII or Unicode mode.

Base64EncodeString(Input[, Flags]) - returns a Base64-formatted string
Base64DecodeToString(Input) - takes a Base64 string and returns a decoded string

Apologies if similar functions have been posted many times before!

Notes:
o EnableExplicit safe
o ASCII/Unicode safe
o Multiple-IncludeFile safe

Code: Select all

; +-------------------+
; | Base64_Helper.pbi |
; +-------------------+
; | 2016.06.24 . Creation (PureBasic 5.42)
; |        .28 . Fixed encode buffer size to prevent overwrite in edge cases
; |        .29 . Decoder now appends '=' padding, if missing
; |        .30 . Added EncodeFile and DecodeToFile functions

CompilerIf (Not Defined(_Base64_Helper_Included, #PB_Constant))
#_Base64_Helper_Included = #True

CompilerIf (#PB_Compiler_IsMainFile)
  EnableExplicit
CompilerEndIf

;-
;- Procedures

Procedure.s Base64EncodeMemory(*Input, InputSize.i, Flags.i = #Null)
  Protected Result.s
  If (*Input And (InputSize > 0))
    Protected OutputSize.i = (InputSize + 2) * 1.34 + 1
    If (OutputSize < 64)
      OutputSize = 64
    EndIf
    Protected *Output = AllocateMemory(OutputSize)
    If (*Output)
      If (Base64Encoder(*Input, InputSize, *Output, OutputSize, Flags))
        Result = PeekS(*Output, OutputSize, #PB_Ascii)
      EndIf
      FreeMemory(*Output)
    EndIf
  EndIf
  ProcedureReturn (Result)
EndProcedure

Procedure.s Base64EncodeString(Input.s, Flags.i = #Null)
  Protected Result.s
  If (Input)
    Protected InputSize = StringByteLength(Input, #PB_UTF8)
    Protected *Input = AllocateMemory(InputSize + 1)
    If (*Input)
      PokeS(*Input, Input, -1, #PB_UTF8)
      Result = Base64EncodeMemory(*Input, InputSize, Flags)
      FreeMemory(*Input)
    EndIf
  EndIf
  ProcedureReturn (Result)
EndProcedure

Procedure.s Base64EncodeFile(InputFile.s, Flags.i = #Null)
  Protected Result.s
  If (InputFile)
    Protected FN.i = ReadFile(#PB_Any, InputFile)
    If (FN)
      Protected Remaining.i = Lof(FN)
      If (Remaining > 0)
        Protected BufferSize.i = 1500000 ; divisble by 3
        Protected *Buffer = AllocateMemory(BufferSize)
        If (*Buffer)
          While (Remaining > 0)
            Protected n.i = BufferSize
            If (n > Remaining)
              n = Remaining
            EndIf
            If (ReadData(FN, *Buffer, n) = n)
              Result + Base64EncodeMemory(*Buffer, n, Flags)
              Remaining - n
            Else
              Result = ""
              Break
            EndIf
          Wend
          FreeMemory(*Buffer)
        EndIf
      EndIf
      CloseFile(FN)
    EndIf
  EndIf
  ProcedureReturn (Result)
EndProcedure

Procedure.i Base64DecodeToMemory(Input.s)
  Protected *Result
  If (Input)
    Select (Len(Input) % 4)
      Case 2 : Input + "=="
      Case 3 : Input + "="
    EndSelect
    Protected InputSize.i = StringByteLength(Input, #PB_Ascii)
    Protected *Input = AllocateMemory(InputSize + 1)
    If (*Input)
      PokeS(*Input, Input, -1, #PB_Ascii)
      Protected OutputSize.i = InputSize + 1
      If (OutputSize < 64)
        OutputSize = 64
      EndIf
      Protected *Output = AllocateMemory(OutputSize)
      If (*Output)
        If (Base64Decoder(*Input, InputSize, *Output, OutputSize))
          CompilerIf (#True)
            Input = RTrim(Input, "=")
            Select (Len(Input) % 4)
              Case 0
                OutputSize = 3 * (Len(Input) / 4) + 0
              Case 2
                OutputSize = 3 * (Len(Input) / 4) + 1
              Case 3
                OutputSize = 3 * (Len(Input) / 4) + 2
            EndSelect
            *Output = ReAllocateMemory(*Output, OutputSize)
          CompilerEndIf
          *Result = *Output
        Else
          FreeMemory(*Output)
        EndIf
      EndIf
      FreeMemory(*Input)
    EndIf
  EndIf
  ProcedureReturn (*Result)
EndProcedure

Procedure.s Base64DecodeToString(Input.s)
  Protected Result.s
  Protected *Output = Base64DecodeToMemory(Input)
  If (*Output)
    Result = PeekS(*Output, MemorySize(*Output), #PB_UTF8 | #PB_ByteLength)
    FreeMemory(*Output)
  EndIf
  ProcedureReturn (Result)
EndProcedure

Procedure.i Base64DecodeToFile(Input.s, OutputFile.s)
  Protected Result.i
  Protected *Output = Base64DecodeToMemory(Input)
  If (*Output)
    Protected FN.i = CreateFile(#PB_Any, OutputFile)
    If (FN)
      Result = WriteData(FN, *Output, MemorySize(*Output))
      CloseFile(FN)
      If (Result <= 0)
        DeleteFile(OutputFile)
      EndIf
    EndIf
    FreeMemory(*Output)
  EndIf
  ProcedureReturn (Result)
EndProcedure

;-
;- Demo Program
CompilerIf (#PB_Compiler_IsMainFile)

DisableExplicit

Debug Base64EncodeString("$")
Debug Base64DecodeToString(Base64EncodeString("$"))
Debug ""

Debug Base64EncodeString("Hello World!")
Debug Base64DecodeToString(Base64EncodeString("Hello World!"))
Debug ""

Debug Base64EncodeString("The quick brown fox jumps over the lazy dog.")
Debug Base64DecodeToString(Base64EncodeString("The quick brown fox jumps over the lazy dog."))
Debug ""

CompilerEndIf
CompilerEndIf
;-

Re: Base64 Helper functions

Posted: Sun Sep 25, 2016 9:27 am
by purenet
This saves a lot of time. Works well with PB 5.50 on macOS.

Thank you :)

Re: Base64 Helper functions

Posted: Sun Sep 25, 2016 9:56 am
by walbus
This things are ever helpfull

Re: Base64 Helper functions

Posted: Mon Sep 26, 2016 10:50 am
by Kwai chang caine
Thanks for sharing 8)