August 20, 2010

Decoding Base64 in SQL

Yesterday (or was it the day before?) I posted a function to encode base64 in SQL. Today I’m posting the function to do the reverse – to turn base64 back into ASCII.

Essentially it works in reverse of the other function. Take four characters, find their ordinal position in the @map variable (e.g. ‘T’ becomes 20) and turn those 24 bits back into 3 characters.

CREATE FUNCTION DecodeBase64(@in varchar(512))
returns varchar(256)
as
begin
declare @map varchar(64)
set @map = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'

declare @out varchar(256)
declare @i int
declare @rem int
declare @one int, @two int, @three int, @four int

set @out = ''
set @i = 1

while @i < len(@in)
begin
 set @one = CHARINDEX(SUBSTRING(@in, @i, 1) COLLATE Latin1_General_CS_AS, @map, 0)-1
 set @i = @i + 1
 set @two = CHARINDEX(SUBSTRING(@in, @i, 1) COLLATE Latin1_General_CS_AS, @map, 0)-1
 set @i = @i + 1

 if SUBSTRING(@in, @i, 1) = '='
 set @three = 0 else
 set @three = CHARINDEX(SUBSTRING(@in, @i, 1) COLLATE Latin1_General_CS_AS, @map, 0)-1
 set @i = @i + 1

 if SUBSTRING(@in, @i, 1) = '='
 set @four = 0
 set @four = CHARINDEX(SUBSTRING(@in, @i, 1) COLLATE Latin1_General_CS_AS, @map, 0)-1
 set @i = @i + 1

 -- @one, @two, @three and @four contain characters from input
 -- now convert them back to 3, 8 bit values
 declare @none int
 declare @none_1 int
 declare @none_2 int
 set @none_1 = @one * 4 -- shl 2
 set @none_2 = ((@two & 48) / 16)
 set @none = @none_1 + @none_2

 declare @ntwo int -- four bits at end of @two
 declare @ntwo_1 int
 declare @ntwo_2 int
 set @ntwo_1 = (@two & 15) * 16
 set @ntwo_2 = ((@three & 60) / 4)
 set @ntwo = @ntwo_1 + @ntwo_2

 declare @nthree int
 declare @nthree_1 int
 declare @nthree_2 int
 set @nthree_1 = (@three & 3) * 64
 set @nthree_2 = @four
 set @nthree = @nthree_1 + @nthree_2

 if @nthree > 0
 set @out = @out + CHAR(@none) + CHAR(@ntwo) + CHAR(@nthree)
 else if @ntwo > 0
 set @out = @out + CHAR(@none) + CHAR(@ntwo)
 else
 set @out = @out + CHAR(@none)

 --print substring(@map, @none, 1)
 -- add on first two bits from second 0x3F
end

return @out

end
Filed under: Uncategorized
Tags: , , , ,
komakino @ 2:41 pm

August 19, 2010

Base 64 Encoding

Recently I had need of a SQL routine to do base64 encoding. It turned out to be an easier task than I had anticipated, given that SQL has no bit shifting capabilities. The code is below. There is no decode routine yet, but writing one should be equally trivial.

Base64 encoding basically works by taking the ASCII values of three consecutive characters (24 bits in total) and turning it into 4 sets of 6 bits (making the highest number possible 63) and then using those 4 numbers as the index into an array of characters which become the encoded text. Where less than three characters are left at the end of the input string, 0 is used for padding and then those characters in the output text are replaced with =.

So ‘ABC’ in ASCII as binary = 01000001 01000010 01000011

Split that into 4 lots of 6 bits and you get

010000 010100 001001 000011

or 16, 20, 9, 3 in decimal.

You then use 16, 20, 9 and 3 as the index into the array:

ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/

and you get QUJD.

CREATE FUNCTION EncodeBase64(@in nvarchar(256))
RETURNS VARCHAR(512)
as
BEGIN

declare @map varchar(64)
set @map = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'

declare @out varchar(512)
declare @i int
declare @rem int
declare @one int, @two int, @three int

set @i = 1
set @out = ''

set @rem = len(@in)
while @rem > 0
begin
 if @rem < 2
 begin
 set @one = ascii(substring(@in, @i, 1))
 set @i = @i + 1
 set @two = 0
 set @i = @i + 1
 set @three = 0
 set @i = @i + 1
 end    else
 if @rem < 3
 begin
 set @one = ascii(substring(@in, @i, 1))
 set @i = @i + 1
 set @two = ascii(substring(@in, @i, 1))
 set @i = @i + 1
 set @three = 0
 set @i = @i + 1
 end else
 begin
 set @one = ascii(substring(@in, @i, 1))
 set @i = @i + 1
 set @two = ascii(substring(@in, @i, 1))
 set @i = @i + 1
 set @three = ascii(substring(@in, @i, 1))
 set @i = @i + 1
 end

 -- Got the three int values, now turn them into 4
 -- take the first 6 bits of @one

 declare @none int
 set @none = (@one & 252) -- first 6 bits of @one (leftmost)
 set @none = @none / 4

 declare @ntwo int
 declare @two_1 int -- left over 2 bits from @one
 set @two_1 = @one & 3

 declare @two_2 int
 set @two_2 = @two & 240    -- first 4 bits of @two
 set @two_2 = @two_2 / 16
 set @ntwo = (@two_1 * 16) + @two_2

 declare @nthree int
 declare @three_1 int
 set @three_1 = @two & 15 -- left over 4 bits from @two
 declare @three_2 int
 set @three_2 = @three & 196 -- first 2 bits of @three
 set @three_2 = @three_2 / 64
 set @nthree = (@three_1 * 4) + @three_2

 declare @nfour int
 set @nfour = @three & 63

 declare @c1 varchar(1), @c2 varchar(1), @c3 varchar(1), @c4 varchar(1)

 if @none > 0
 set @c1 = substring(@map, @none+1, 1)
 else set @c1 = '='

 if @ntwo > 0
 set @c2 = substring(@map, @ntwo+1, 1)
 else set @c2 = '='

 if @nthree > 0
 set @c3 = substring(@map, @nthree+1, 1)
 else set @c3 = '='

 if @nfour > 0
 set @c4 = substring(@map, @nfour+1, 1)
 else set @c4 = '='

 set @out = @out + @c1 + @c2 + @c3 + @c4

 set @rem = len(@in) - @i + 1
end

return @out

end

This is not the best, or even only, way of accomplishing this, but this is my way of doing it :)

Filed under: Uncategorized
Tags: , , , ,
komakino @ 2:53 pm

x