I am trying to base64 decode a value (16-bit signed big endian) from a Kafka <> Debezium Payload using a User Defined Function in Snowflake. Unfortunately, I am only able to use Javascript and Snowflake uses Javascript Standard Library. I found a working example for NodeJS which uses the Buffer module but that is not usable in Javascript.
var kafkaDecodeNumber = function(base64EncodedNumber) {
var byteArray = Buffer.from(base64EncodedNumber, 'base64');
var value = 0;
for (var i = 0; i < byteArray.length; i++) {
value = (value * 256) + byteArray[i];
}
return value;
};
var float = parseFloat(kafkaDecodeNumber( price )).toFixed(38) / 100
Here are some working code examples I am using elsewhere
Python
ctx = decimal.Context()
ctx.prec = 38
result = ctx.create_decimal(
int.from_bytes(base64.b64decode(byte_value), byteorder='big')
) / 100
return result
Ruby
event.get('price').unpack1('m*').unpack1('B*').to_i(2) / 100.0)
Any assistance here would be great!
Here's how you can do it using atob (polyfill as required) Uint8Array and DataView
Note, the raw data
array is "filled" to be an even length - but the last two bytes need to be swapped, I believe
var kafkaDecodeNumber = function(base64EncodedNumber) {
var data = atob(base64EncodedNumber).split('').map(v => v.charCodeAt(0));
if (data.length % 2) {
data.push(0, data.pop());
}
var uint8 = new Uint8Array(data);
return new DataView(uint8.buffer).getInt16(0);
}
var float = parseFloat(kafkaDecodeNumber( 'D6A=' )).toFixed(38) / 100;
console.log(float);
float = parseFloat(kafkaDecodeNumber( '/w==' )).toFixed(38) / 100;
console.log(float);
Actually, since the code will only ever get an encoded int16 - the code is far simpler
var kafkaDecodeNumber = function(base64EncodedNumber) {
const [a, b] = atob(base64EncodedNumber).split('').map(v => v.charCodeAt(0));
return b === undefined ? a : a * 256 + b;
}
var float = parseFloat(kafkaDecodeNumber( 'D6A=' )).toFixed(38) / 100;
console.log(float);
float = parseFloat(kafkaDecodeNumber( '/w==' )).toFixed(38) / 100;
console.log(float);
Actually, just figured out the easiest way, given you'll only ever get a 16 bit number passed in - and this way doesn't need any base64 polyfill
var kafkaDecodeNumber = function(base64EncodedNumber) {
const b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
const bits = base64EncodedNumber
.replace(/=+$/g, '')
.split('')
.reduce((a, b) => a + b64.indexOf(b).toString(2).padStart(6, '0'), '');
return parseInt(bits.match(/[01]{8}/g).join('').padStart(16, '0'), 2);
}
var float = parseFloat(kafkaDecodeNumber( 'D6A=' )).toFixed(38) / 100;
console.log(float);
float = parseFloat(kafkaDecodeNumber( '/w==' )).toFixed(38) / 100;
console.log(float);