I’m not having a great day finding information. What is the equivalent in EncodeBase64/DecodeBase64 in the new framework?
There is none. I needed encodeBase64, so I made my own. It is in the XojoiOSWrapper GitHub - Mitchboo/XojoiOSWrapper: Module that brings legacy and additional functions to Xojo iOS
I built it with the info at Base64 - Wikipedia . It is not entirely bullet proof, though. Pass 10K data, it crashes randomly. I don’t know if it is dues to memory management or something else, and plan on coming back to it soon, as I need it to process >20K files in a future project.
DecodeBase64 should not be terribly complicated to achieve.
Ug. Seems like a major oversight. I would expect that to be added in another round.
Thanks for the info. For now I’ll use your method.
I can’t seem to find it in the project. I’m not on my regular development computer today but I’ll look at it again tomorrow.
Function EncodeBase64(Extends t As Text) As Text
Declare Function dataWithBytes Lib “UIKit” Selector “dataWithBytes:length:” (class_id As Ptr, bytes As Ptr, length As UInt32) As Ptr
Declare Function base64EncodedStringWithOptions Lib “UIKit” Selector “base64EncodedStringWithOptions:” (class_id As Ptr, options As UInt32) As CFStringRef
// Create NSData pointer to be point of reference.
Dim data As Ptr
data = NSClassFromString(“NSData”)
// Create global ASCII TextEncoding
Dim te As Xojo.Core.TextEncoding
te = Xojo.Core.TextEncoding.FromIANAName(“ISO-8859-1”)
// Convert Text to MemoryBlock
Dim tmb As Xojo.Core.MemoryBlock
tmb = te.ConvertTextToData(t)
// Create NSData object using MemoryBlock
Dim dataRef as Ptr = dataWithBytes(data, tmb.Data, tmb.Size)
// Create Text object to hold Base64 encoded string.
Dim x As Text
x = base64EncodedStringWithOptions(dataRef, 0)
// Return Base64 encoded string
Return x
End Function
Enjoy!
Philip, do you have a decode version as well that you’d be willing to share?
Encode/Decode using declares:
Add a constant “FoundationLib = Foundation.framework”
[code]
Function decodeBase64(aText as text) As Text
declare function initWithBase64EncodedString lib FoundationLib selector “initWithBase64EncodedString:options:” _
(obj_id as ptr, str as CFStringRef, options as Integer) as ptr
declare function alloc lib FoundationLib selector “alloc” (clsRef as ptr) as ptr
declare function NSClassFromString lib FoundationLib (clsName as CFStringRef) as ptr
dim mData as ptr = initWithBase64EncodedString(alloc(NSClassFromString(“NSData”)), aText, 1)
const NSUTF8StringEncoding = 4
declare function initWithData lib FoundationLib selector “initWithData:encoding:” (obj_id as ptr, data as ptr, encoding as Integer) as CFStringRef
Return initWithData(alloc(NSClassFromString(“NSString”)), mData, NSUTF8StringEncoding)
End Function[/code]
Function encodeBase64(aText as Text) As Text
declare function dataUsingEncoding lib FoundationLib selector "dataUsingEncoding:" (obj_id as ptr, encoding as Integer) as ptr
declare function stringWithString lib FoundationLib selector "stringWithString:" (obj_id as ptr, str as CFStringRef) as ptr
declare function NSClassFromString lib FoundationLib (clsName as CFStringRef) as ptr
const NSUTF8StringEncoding = 4
dim str as Ptr = stringWithString(NSClassFromString("NSString"), aText)
dim mData as ptr = dataUsingEncoding(str,NSUTF8StringEncoding)
declare function base64EncodedStringWithOptions lib FoundationLib selector "base64EncodedStringWithOptions:" _
(obj_id as ptr, options as Integer) as CFStringRef
Return base64EncodedStringWithOptions(mData,1)
End Function
Honestly no because I haven’t needed to decode yet.
I’ve spent like two weeks writing an abstraction/porting library. Too much code doesn’t work between Desktop/iOS. So I have replaced all the TCP/HTTP sockets, Timers, EasyTCPSockets, etc.
They mimic the new framework exactly but gracefully downgrade to the old framework on Desktop apps.
[quote=157021:@Phillip Zedalis]Function EncodeBase64(Extends t As Text) As Text
Enjoy![/quote]
Hey, that is neat
Thank you Phillip !
[quote=157024:@Jason King]Encode/Decode using declares:
[/quote]
Whaoo !
Great ! Thank you Jason.
Challenge accepted!
Ever interested in learning, I came up with this. I confirmed that it works in iOS, although I have not done extensive testing against the native Xojo methods:
Encode:
Protected Function EncodeBase64(t As Text) As Text
if t.Empty then
return ""
end if
dim lookup() as Text = Base64LookupTable
dim maskByte1 as UInt8 = &b11111100
dim maskByte2 as UInt16 = &b0000001111110000
dim maskByte3 as UInt16 = &b0000111111000000
dim maskByte4 as Uint8 = &b00111111
//
// Pad the text if needed
//
dim nullCount as integer = t.Length mod 3
if nullCount <> 0 then
nullCount = 3 - nullCount
t = t + Text.FromUnicodeCodepoint( 0 )
end if
dim chars() as text
dim mb as MemoryBlock
#if TargetIOS then
mb = Xojo.Core.TextEncoding.UTF8.ConvertTextToData( t )
#else
mb = t
#endif
mb.LittleEndian = false
dim lastByteIndex as integer = mb.Size - 1
for masterIndex as integer = 0 to lastByteIndex step 3
dim byteIndex as integer = masterIndex
dim lookupIndex as integer = mb.UInt8Value( byteIndex ) and maskByte1
lookupIndex = lookupIndex \\ 4
chars.Append lookup( lookupIndex )
if byteIndex = lastByteIndex then
exit
end if
lookupIndex = mb.UInt16Value( byteIndex ) and maskByte2
lookupIndex = lookupIndex \\ 16
chars.Append lookup( lookupIndex )
byteIndex = byteIndex + 1
if byteIndex = lastByteIndex then
exit
end if
lookupIndex = mb.UInt16Value( byteIndex ) and maskByte3
lookupIndex = lookupIndex \\ 64
chars.Append lookup( lookupIndex )
byteIndex = byteIndex + 1
lookupIndex = mb.UInt8Value( byteIndex ) and maskByte4
chars.Append lookup( lookupIndex )
next
if nullCount = 2 then
chars.Append "=="
elseif nullCount = 1 then
chars( chars.Ubound ) = "="
end if
dim whole as Text = Text.Join( chars, "" )
dim sections() as Text
dim lastTextIndex as integer = whole.Length - 1
for i as integer = 0 to lastTextIndex step 76
dim lineLen as integer = if( i + 76 <= lastTextIndex, 76, lastTextIndex - i + 1 )
sections.Append whole.Mid( i, lineLen )
next i
static eol as Text = Text.FromUnicodeCodepoint( 13 ) + Text.FromUnicodeCodepoint( 10 )
dim r as text = Text.Join( sections, eol )
return r
End Function
Private Function Base64LookupTable() As Text()
static r() as text
if r.Ubound = -1 then
dim bunch as Text = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
for each char as text in bunch.Characters
r.Append char
next
end if
return r
End Function
Decode:
Protected Function DecodeBase64(encoded As Text) As MemoryBlock
static last4BitsMask as UInt8 = &b00001111
static last2BitsMask as UInt8 = &b00000011
if encoded.Empty then
return nil
end if
//
// Get rid of the EOL
//
encoded = encoded.ReplaceAll( Text.FromUnicodeCodepoint( 10 ), "" )
encoded = encoded.ReplaceAll( Text.FromUnicodeCodepoint( 13 ), "" )
if encoded.Length mod 4 <> 0 then
raise new OutOfBoundsException
end if
dim nullCount as integer
if encoded.Right( 2 ) = "==" then
nullCount = 2
elseif encoded.Right( 1 ) = "=" then
nullCount = 1
end if
dim mb as MemoryBlock = TextEncoding.UTF8.ConvertTextToData( encoded )
dim result as new MemoryBlock( ( mb.Size * 3 / 4 ) - nullCount )
dim resultIndex as integer
dim lastByteIndex as integer = mb.Size - 1
dim sourcePtr as Ptr = mb.Data
dim resultPtr as Ptr = result.Data
for i as integer = 0 to lastByteIndex step 4
dim raw1 as UInt8 = sourcePtr.Byte( i )
dim raw2 as UInt8 = sourcePtr.Byte( i + 1 )
dim raw3 as UInt8 = sourcePtr.Byte( i + 2 )
dim raw4 as UInt8 = sourcePtr.Byte( i + 3 )
dim lookup1 as UInt8 = Base64ValueOf( raw1 )
dim lookup2 as UInt8 = Base64ValueOf( raw2 )
dim lookup3 as UInt8 = Base64ValueOf( raw3 )
dim lookup4 as UInt8 = Base64ValueOf( raw4 )
dim thisByte as UInt8
thisByte = ( lookup1 * 4 ) + ( lookup2 \\ 16 )
resultPtr.Byte( resultIndex ) = thisByte
resultIndex = resultIndex + 1
if resultIndex >= result.Size then
exit
end if
dim last4Bits as UInt8 = lookup2 and last4BitsMask
dim first4Bits as UInt8 = lookup3 \\ 4
thisByte = ( last4Bits * 16 ) + first4Bits
resultPtr.Byte( resultIndex ) = thisByte
resultIndex = resultIndex + 1
if resultIndex >= result.Size then
exit
end if
dim last2Bits as UInt8 = lookup3 and last2BitsMask
thisByte = ( last2Bits * 64 ) + lookup4
resultPtr.Byte( resultIndex ) = thisByte
resultIndex = resultIndex + 1
next
return result
End Function
Private Function Base64ValueOf(byteValue As Integer) As UInt8
select case byteValue
case 65 to 90 // A to Z
return byteValue - 65
case 97 to 122 // a to z
return 26 + ( byteValue - 97 )
case 48 to 57 // 0 - 9
return 52 + ( byteValue - 48 )
case 43 // +
return 62
case 47 // /
return 63
case 61 // =
return 0
else
raise new OutOfBoundsException
end
End Function
Thanks for the responses guys. However, with both the iOS and Xojo code it doesn’t work well with large amounts of data. Not quite sure what’s going on.
Here’s my current task: I’m sending data from Xojo desktop app via a JSON string that’s encoded to an iOS app. The JSON string is kind of similar to what EasyTCP does with a Command and Data but before I send it I Base64 encoded and add my end of message string.
When I send simple data there’s no issue. When I send something larger (like an image) that’s when things start to go wonky with either DecodeBase64 solution. With the Xojo solution I get an out of bounds exceptions on large data. With the iOS declares I get blank strings back and it doesn’t have to be quite as big. I am limiting my data chunks to 500 KB at a time but those sizes seem to overwhelm iOS.
Any advice on where to go next?
[quote=159755:@Bob Keeney]Thanks for the responses guys. However, with both the iOS and Xojo code it doesn’t work well with large amounts of data. Not quite sure what’s going on.
Here’s my current task: I’m sending data from Xojo desktop app via a JSON string that’s encoded to an iOS app. The JSON string is kind of similar to what EasyTCP does with a Command and Data but before I send it I Base64 encoded and add my end of message string.
When I send simple data there’s no issue. When I send something larger (like an image) that’s when things start to go wonky with either DecodeBase64 solution. With the Xojo solution I get an out of bounds exceptions on large data. With the iOS declares I get blank strings back and it doesn’t have to be quite as big. I am limiting my data chunks to 500 KB at a time but those sizes seem to overwhelm iOS.
Any advice on where to go next?[/quote]
I had written a Xojo encodeBase64 during beta which worked perfectly for small files but exploded when data was too big as well. Seems memory management loses its marbles past a certain size.
I would have hopped the declare methods t work better, as they make use of Apple own framework…
I shall use them soon in a coming app, but file size will probably never exceed 200 K (font files).
Yeah, I started off using Kem’s code which worked great for small amounts of data. After it failed with large image files I switched to the declare version and it fails on even smaller files.
Major drag to not have a stable and safe way to do this. That immediately kills two or three of my upcoming projects.
I wonder if all that could not be related to memory management. That OutOfMemoryException is particularly worrysome. It seems iOS is somewhat less flexible than Mac OS X.
Kem’s method, like mine, makes use of a lot of operations on memoryblocks, and this in particular may explain :
Now, how would one do some house cleaning to prevent issues ?
I bet one of you “declare” experts can do something with this…
Swift Code
let plainString = “foo”
[h]Encoding[/h]
let plainData = plainString.dataUsingEncoding(NSUTF8StringEncoding)
let base64String = plainData?.base64EncodedStringWithOptions(.allZeros)
println(base64String!) // Zm9v
[h]Decoding[/h]
let decodedData = NSData(base64EncodedString: base64String!, options: .allZeros)
let decodedString = NSString(data: decodedData, encoding: NSUTF8StringEncoding)
println(decodedString) // foo
[quote=159773:@Dave S]I bet one of you “declare” experts can do something with this…
Swift Code
let plainString = “foo”
[h]Encoding[/h]
let plainData = plainString.dataUsingEncoding(NSUTF8StringEncoding)
let base64String = plainData?.base64EncodedStringWithOptions(.allZeros)
println(base64String!) // Zm9v
[h]Decoding[/h]
let decodedData = NSData(base64EncodedString: base64String!, options: .allZeros)
let decodedString = NSString(data: decodedData, encoding: NSUTF8StringEncoding)
println(decodedString) // foo
[/quote]
That is exactly what Jason King did
What about looping through and appending the results to a file in chunks? If you split your string into, say, chunks of 5700 bytes, encode those and append it to a file, the memory limitations shouldn’t come into play.
(I came up with 5700 this way: Each encoded line is 76 characters, and 3/4 of 76 is 57, so each line represents 57 bytes of data. 100 lines represents 5700 bytes of data.)
[quote=159779:@Kem Tekinay]What about looping through and appending the results to a file in chunks? If you split your string into, say, chunks of 5700 bytes, encode those and append it to a file, the memory limitations shouldn’t come into play.
(I came up with 5700 this way: Each encoded line is 76 characters, and 3/4 of 76 is 57, so each line represents 57 bytes of data. 100 lines represents 5700 bytes of data.)[/quote]
57 will work. Divisible by 3, it won’t produce any = at the end, so the chunks can be concatenated. Only the last chunk may be less, and the = will then not be an issue. Excellent idea, Kem
I just hope garbage collection is efficient, and that manipulating memoryblocks that many times will not leak.
In a desktop app, I’d recommend reusing the same MemoryBlocks and writing the chunks to them with StringValue. I don’t see how to do the equivalent in an iOS project.