Base64 Helper functions

Share your advanced PureBasic knowledge/code with the community.
User avatar
kenmo
Addict
Addict
Posts: 2047
Joined: Tue Dec 23, 2003 3:54 am

Base64 Helper functions

Post 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
;-
purenet
User
User
Posts: 30
Joined: Wed Oct 21, 2009 10:11 am

Re: Base64 Helper functions

Post by purenet »

This saves a lot of time. Works well with PB 5.50 on macOS.

Thank you :)
walbus
Addict
Addict
Posts: 929
Joined: Sat Mar 02, 2013 9:17 am

Re: Base64 Helper functions

Post by walbus »

This things are ever helpfull
User avatar
Kwai chang caine
Always Here
Always Here
Posts: 5494
Joined: Sun Nov 05, 2006 11:42 pm
Location: Lyon - France

Re: Base64 Helper functions

Post by Kwai chang caine »

Thanks for sharing 8)
ImageThe happiness is a road...
Not a destination
Post Reply