Why do EnumPrintersA and EnumPrintersW request the same amount of memory? - javascript

I call EnumPrintersA/EnumPrintersW functions using node-ffi to get list of local printers accessible from my PC.
You should create the buffer which will be filled with information by EnumPrinters function.
But you do not know the required size of the buffer.
In this case you need to execute EnumPrintersA/EnumPrintersW twice.
During the first call this function calculates the amount of memory for information about printers, during the second call this function fills the buffer with information about printers.
In case of Unicode version of EnumPrinters function, each letter in printers name will be encoded using two characters in Windows.
Why the first call to EnumPrintersW returns the same required amount of memory as the first call to EnumPrintersA?
Unicode strings are twice as long as not-unicode strings, but required buffer size is the same.
var ffi = require('ffi')
var ref = require('ref')
var Struct = require('ref-struct')
var wchar_t = require('ref-wchar')
var int = ref.types.int
var intPtr = ref.refType(ref.types.int)
var wchar_string = wchar_t.string
var getPrintersA = function getPrinters() {
var PRINTER_INFO_4A = Struct({
'pPrinterName' : ref.types.CString,
'pServerName' : ref.types.CString,
'Attributes' : int
});
var printerInfoPtr = ref.refType(PRINTER_INFO_4A);
var winspoolLib = new ffi.Library('winspool', {
'EnumPrintersA': [ int, [ int, ref.types.CString, int, printerInfoPtr, int, intPtr, intPtr ] ]
});
var pcbNeeded = ref.alloc(int, 0);
var pcReturned = ref.alloc(int, 0);
//Get amount of memory for the buffer with information about printers
var res = winspoolLib.EnumPrintersA(6, ref.NULL, 4, ref.NULL, 0, pcbNeeded, pcReturned);
if (res != 0) {
console.log("Cannot get list of printers. Error during first call to EnumPrintersA. Error: " + res);
return;
}
var bufSize = pcbNeeded.deref();
var buf = Buffer.alloc(bufSize);
console.log(bufSize);
//Fill buf with information about printers
res = winspoolLib.EnumPrintersA(6, ref.NULL, 4, buf, bufSize, pcbNeeded, pcReturned);
if (res == 0) {
console.log("Cannot get list of printers. Eror: " + res);
return;
}
var countOfPrinters = pcReturned.deref();
var printers = Array(countOfPrinters);
for (var i = 0; i < countOfPrinters; i++) {
var pPrinterInfo = ref.get(buf, i*PRINTER_INFO_4A.size, PRINTER_INFO_4A);
printers[i] = pPrinterInfo.pPrinterName;
}
return printers;
};
var getPrintersW = function getPrinters() {
var PRINTER_INFO_4W = Struct({
'pPrinterName' : wchar_string,
'pServerName' : wchar_string,
'Attributes' : int
});
var printerInfoPtr = ref.refType(PRINTER_INFO_4W);
var winspoolLib = new ffi.Library('winspool', {
'EnumPrintersW': [ int, [ int, wchar_string, int, printerInfoPtr, int, intPtr, intPtr ] ]
});
var pcbNeeded = ref.alloc(int, 0);
var pcReturned = ref.alloc(int, 0);
//Get amount of memory for the buffer with information about printers
var res = winspoolLib.EnumPrintersW(6, ref.NULL, 4, ref.NULL, 0, pcbNeeded, pcReturned);
if (res != 0) {
console.log("Cannot get list of printers. Error during first call to EnumPrintersW. Eror code: " + res);
return;
}
var bufSize = pcbNeeded.deref();
var buf = Buffer.alloc(bufSize);
console.log(bufSize);
//Fill buf with information about printers
res = winspoolLib.EnumPrintersW(6, ref.NULL, 4, buf, pcbNeeded.deref(), pcbNeeded, pcReturned);
if (res == 0) {
console.log("Cannot get list of printers. Eror code: " + res);
return;
}
var countOfPrinters = pcReturned.deref();
var printers = new Array(countOfPrinters);
for (var i = 0; i < countOfPrinters; i++) {
var pPrinterInfo = ref.get(buf, i*PRINTER_INFO_4W.size, PRINTER_INFO_4W);
printers[i] = pPrinterInfo.pPrinterName;
}
return printers;
};
https://msdn.microsoft.com/ru-ru/library/windows/desktop/dd162692(v=vs.85).aspx
BOOL EnumPrinters(
_In_ DWORD Flags,
_In_ LPTSTR Name,
_In_ DWORD Level,
_Out_ LPBYTE pPrinterEnum,
_In_ DWORD cbBuf,
_Out_ LPDWORD pcbNeeded,
_Out_ LPDWORD pcReturned
);
https://msdn.microsoft.com/ru-ru/library/windows/desktop/dd162847(v=vs.85).aspx
typedef struct _PRINTER_INFO_4 {
LPTSTR pPrinterName;
LPTSTR pServerName;
DWORD Attributes;
} PRINTER_INFO_4, *PPRINTER_INFO_4;

I can confirm that what you found with EnumPrintersA and EnumPrintersW is reproducible.
In my machine, they both require 240 bytes.
This got me curious, so I decided to allocate a separate buffer for each function and dump each buffer to a file and opened them with a hex editor.
The interesting part of each file is of course the names of the printers.
To keep this short, I'll show you the first 3 names of the printers.
The first line is from EnumPrintersA, the second is from EnumPrintersW:
Fax.x...FX DocuPrint C1110 PCL 6..C.1.1.1.0. .P.C.L. .6...Microsoft XPS Document Writer.o.c.u.m.e.n.t. .W.r.i.t.e.r...
F.a.x...F.X. .D.o.c.u.P.r.i.n.t. .C.1.1.1.0. .P.C.L. .6...M.i.c.r.o.s.o.f.t. .X.P.S. .D.o.c.u.m.e.n.t. .W.r.i.t.e.r...
From this result, it appears that EnumPrintersA calls EnumPrintersW for the actual work and then simply converts each string in the buffer to single byte characters and puts the resulting string in the same place.
To confirm this, I decided to trace EnumPrintersA code and I found that it definitely calls EnumPrintersW at position winspool.EnumPrintersA + 0xA7.
The actual position is likely different in a different Windows version.
This got me even more curious, so I decided to test other functions that have A and W versions.
This is what I found:
EnumMonitorsA 280 bytes needed
EnumMonitorsW 280 bytes needed
EnumServicesStatusA 20954 bytes needed
EnumServicesStatusW 20954 bytes needed
EnumPortsA 2176 bytes needed
EnumPortsW 2176 bytes needed
EnumPrintProcessorsA 24 bytes needed
EnumPrintProcessorsW 24 bytes needed
From this result, my conclusion is that EnumPrintersA calls EnumPrintersW for the actual work and converts the string in the buffer and other functions that have A and W versions also do the same thing.
This appears to be a common mechanism to avoid duplication of code in expense of larger buffers, maybe because buffers can be deallocated anyway.

At the beginning I thought that there's something wrong with your code, so I kept looking for a mistake (introduced by the FFI or JS layers, or a typo or something similar), but I couldn't find anything.
Then, I started to write a program similar to yours in C (to eliminate any extra layers that could introduce errors).
main.c:
#include <stdio.h>
#include <Windows.h>
#include <conio.h> // !!! Deprecated!!!
typedef BOOL (__stdcall *EnumPrintersAFuncPtr)(_In_ DWORD Flags, _In_ LPSTR Name, _In_ DWORD Level, _Out_ LPBYTE pPrinterEnum, _In_ DWORD cbBuf, _Out_ LPDWORD pcbNeeded, _Out_ LPDWORD pcReturned);
typedef BOOL (__stdcall *EnumPrintersWFuncPtr)(_In_ DWORD Flags, _In_ LPWSTR Name, _In_ DWORD Level, _Out_ LPBYTE pPrinterEnum, _In_ DWORD cbBuf, _Out_ LPDWORD pcbNeeded, _Out_ LPDWORD pcReturned);
void testFunc()
{
PPRINTER_INFO_4A ppi4a = NULL;
PPRINTER_INFO_4W ppi4w = NULL;
BOOL resa, resw;
DWORD neededa = 0, returneda = 0, neededw = 0, returnedw = 0, gle = 0, i = 0, flags = PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS;
LPBYTE bufa = NULL, bufw = NULL;
resa = EnumPrintersA(flags, NULL, 4, NULL, 0, &neededa, &returneda);
if (resa) {
printf("EnumPrintersA(1) succeeded with NULL buffer. Exiting...\n");
return;
} else {
gle = GetLastError();
if (gle != ERROR_INSUFFICIENT_BUFFER) {
printf("EnumPrintersA(1) failed with %d(0x%08X) which is different than %d. Exiting...\n", gle, gle, ERROR_INSUFFICIENT_BUFFER);
return;
} else {
printf("EnumPrintersA(1) needs a %d(0x%08X) bytes long buffer.\n", neededa, neededa);
}
}
resw = EnumPrintersW(flags, NULL, 4, NULL, 0, &neededw, &returnedw);
if (resw) {
printf("EnumPrintersW(1) succeeded with NULL buffer. Exiting...\n");
return;
} else {
gle = GetLastError();
if (gle != ERROR_INSUFFICIENT_BUFFER) {
printf("EnumPrintersW(1) failed with %d(0x%08X) which is different than %d. Exiting...\n", gle, gle, ERROR_INSUFFICIENT_BUFFER);
return;
} else {
printf("EnumPrintersW(1) needs a %d(0x%08X) bytes long buffer.\n", neededw, neededw);
}
}
bufa = (LPBYTE)calloc(1, neededa);
if (bufa == NULL) {
printf("calloc failed with %d(0x%08X). Exiting...\n", errno, errno);
return;
} else {
printf("buffera[0x%08X:0x%08X]\n", (long)bufa, (long)bufa + neededa - 1);
}
bufw = (LPBYTE)calloc(1, neededw);
if (bufw == NULL) {
printf("calloc failed with %d(0x%08X). Exiting...\n", errno, errno);
free(bufa);
return;
} else {
printf("bufferw[0x%08X:0x%08X]\n", (long)bufw, (long)bufw + neededw - 1);
}
resa = EnumPrintersA(flags, NULL, 4, bufa, neededa, &neededa, &returneda);
if (!resa) {
gle = GetLastError();
printf("EnumPrintersA(2) failed with %d(0x%08X). Exiting...\n", gle, gle);
free(bufa);
free(bufw);
return;
}
printf("EnumPrintersA(2) copied %d bytes in the buffer out of which the first %d(0x%08X) represent %d structures of size %d\n", neededa, returneda * sizeof(PRINTER_INFO_4A), returneda * sizeof(PRINTER_INFO_4A), returneda, sizeof(PRINTER_INFO_4A));
resw = EnumPrintersW(flags, NULL, 4, bufw, neededw, &neededw, &returnedw);
if (!resw) {
gle = GetLastError();
printf("EnumPrintersW(2) failed with %d(0x%08X). Exiting...\n", gle, gle);
free(bufw);
free(bufa);
return;
}
printf("EnumPrintersW(2) copied %d bytes in the buffer out of which the first %d(0x%08X) represent %d structures of size %d\n", neededw, returnedw * sizeof(PRINTER_INFO_4W), returnedw * sizeof(PRINTER_INFO_4W), returnedw, sizeof(PRINTER_INFO_4W));
ppi4a = (PPRINTER_INFO_4A)bufa;
ppi4w = (PPRINTER_INFO_4W)bufw;
printf("\nPrinting ASCII results:\n");
for (i = 0; i < returneda; i++) {
printf(" Item %d\n pPrinterName: [%s]\n", i, ppi4a[i].pPrinterName ? ppi4a[i].pPrinterName : "NULL");
}
printf("\nPrinting WIDE results:\n");
for (i = 0; i < returnedw; i++) {
wprintf(L" Item %d\n pPrinterName: [%s]\n", i, ppi4w[i].pPrinterName ? ppi4w[i].pPrinterName : L"NULL");
}
free(bufa);
free(bufw);
}
int main()
{
testFunc();
printf("\nPress a key to exit...\n");
getch();
return 0;
}
Note: in terms of variable names (I kept them short - and thus not very intuitive), the a or w at the end of their names means that they are used for ASCII / WIDE version.
Initially, I was afraid that EnumPrinters might not return anything, since I'm not connected to any printer at this point, but luckily I have some (7 to be more precise) "saved". Here's the output of the above program (thank you #qxz for correcting my initial (and kind of faulty) version):
EnumPrintersA(1) needs a 544(0x00000220) bytes long buffer.
EnumPrintersW(1) needs a 544(0x00000220) bytes long buffer.
buffera[0x03161B20:0x03161D3F]
bufferw[0x03165028:0x03165247]
EnumPrintersA(2) copied 544 bytes in the buffer out of which the first 84(0x00000054) represent 7 structures of size 12
EnumPrintersW(2) copied 544 bytes in the buffer out of which the first 84(0x00000054) represent 7 structures of size 12
Printing ASCII results:
Item 0
pPrinterName: [Send To OneNote 2013]
Item 1
pPrinterName: [NPI060BEF (HP LaserJet Professional M1217nfw MFP)]
Item 2
pPrinterName: [Microsoft XPS Document Writer]
Item 3
pPrinterName: [Microsoft Print to PDF]
Item 4
pPrinterName: [HP Universal Printing PCL 6]
Item 5
pPrinterName: [HP LaserJet M4345 MFP [7B63B6]]
Item 6
pPrinterName: [Fax]
Printing WIDE results:
Item 0
pPrinterName: [Send To OneNote 2013]
Item 1
pPrinterName: [NPI060BEF (HP LaserJet Professional M1217nfw MFP)]
Item 2
pPrinterName: [Microsoft XPS Document Writer]
Item 3
pPrinterName: [Microsoft Print to PDF]
Item 4
pPrinterName: [HP Universal Printing PCL 6]
Item 5
pPrinterName: [HP LaserJet M4345 MFP [7B63B6]]
Item 6
pPrinterName: [Fax]
Press a key to exit...
Amazingly (at least for me), the behavior you described could be reproduced.
Note that the above output is from the 032bit compiled version of the program (064bit pointers are harder to read :) ), but the behavior is reproducible when building for 064bit as well (I am using VStudio 10.0 on Win10).
Since there are for sure strings at the end of the buffer, I started debugging:
Above is a picture of VStudio 10.0 Debug window, with the program interrupted at the end of testFunc, just before freeing the 1st pointer. Now, I don't know how familiar are you with debugging on VStudio, so I'm going to walk through the (relevant) window areas:
At the bottom, there are 2 Watch windows (used to display variables while the program is running). As seen, the variable Name, Value and Type are displayed
At the right, (Watch 1): the 1st (0th) and the last (6th - as there are 7) of the structures at the beginning of each of the 2 buffers
At the left, (Watch 2): the addresses of the 2 buffers
Above the Watch windows, (Memory 2) is the memory content for bufw. A Memory window contains a series of rows and in each row there's the memory address (grayed, at the left), followed by its contents in hex (each byte corresponds to 2 hex digits - e.g. 1E), then at the right the same contents in char representation (each byte corresponds to 1 char - I'm going to come back on this), then the next row, and so on
Above Memory 2, (Memory 1): it's the memory content for bufa
Now, going back to the memory layout: not all the chars at the right are necessarily what they seem, some of them are just displayed like that for human readability. For example there are a lot of dots (.) on the right side, but they are not all dots. If you look for a dot at the corresponding hex representation, you'll notice that for many of them it's 00 or NULL (which is a non printable char, but it's displayed as a dot).
Regarding the buffer contents each of the 2 Memory windows (looking at the char representation), there are 3 zones:
The PRINTER_INFO_4* zone or the gibberish at the beginning: 544 bytes corresponding to approximately the 1st 3 rows
The funky chars from the last ~1.5 rows: they are outside of our buffers so we don't care about them
The mid zone: where the strings are stored
Let's look at the WIDE strings zone (Memory 2 - mid zone): as you mentioned, each character has 2 bytes: because in my case they're all ASCII chars, the MSB (or the codepage byte) is always 0 (that's why you see chars and dots interleaved: e.g. ".L.a.s.e.r.J.e.t" in row 4).
Since there are multiple strings in the buffer (or string, if you will) - or even better: multiple TCHAR*s in a TCHAR* - they must be separated: that is done by a NULL WIDE char (hex: 00 00, char: "..") at the end of each string; combined with the fact that the next string's 1st byte (char) is also 00 (.), you'll see a sequence of 3 NULL bytes (hex: 00 00 00, char: "...") and that is the separator between 2 (WIDE) strings in the mid zone.
Now, comparing the 2 mid parts (corresponding to the 2 buffers), you'll notice that the string separators are exactly in the same positions and more: the last parts of each string are the also same (the last halves of each string to be more precise).
Considering this, here's my theory:
I think EnumPrintersA calls EnumPrintersW, and then it iterates through each of the strings (at the end of the buffer), and calls wcstombs or even better: [MS.Docs]: WideCharToMultiByte function on them (converting them in place - and thus the resulting ASCII string only takes the 1st half of the WIDE string, leaving the 2nd half unmodified), without converting all the buffer. I'll have to verify this by looking with a disassembler in winspool.drv.
Personally (if I'm right) I think that it is a lame workaround (or a gainarie as I like to call it), but who knows, maybe all the *A, *W function pairs (at least those who return multiple char*s in a char*) work like this. Anyway, there are also pros for this approach (at least for these 2 funcs):
dev-wise: it's OK for one function to call the other and keep the implementation in 1 place (instead of duping it in both functions)
performance-wise: it's OK not to recreate the buffer since that would
imply additional computation; after all, the buffer consumer doesn't normally reach the second halves of each ASCII string in the buffer

Related

NodeJS: Capturing a stereo PCM wave stream into mono AudioBuffer

I'm recording audio from nodejs using node-microphone (which is just a javascript interface for arecord), and want to store the stream chunks in an AudioBuffer using web-audio-api (which is a nodejs implementation of the Web Audio API).
My audio source has two channels while my AudioBuffer has only one (in purpose).
This is my working configuration for recording audio with arecord through my USB sound card (I'm using a Raspberry pi 3 running on Raspbian buster):
arecord -D hw:1,0 -c 2 -f S16_LE -r 44100
Running this command with an output path and playing the resulting wav file with aplay works just fine. So node-microphone is able to record audio with these parameters, and at the end I get a nodejs readable stream flowing wave data.
But
I'm struggling doing the bridge from the stream chunks (Buffer instances) to the AudioBuffer. More precisely; I'm not sure of the format of the incoming data, not sure of the destination format, and not sure of how I would do the conversion whatever:
The stream chunks are Buffers so they also are Uint8Arrays. Regarding my configuration, I guess they are binary representations of 16 bits signed integers (little endian, I don't know what it means).
The AudioBuffer holds multiple buffers (one per channel, so only one in my case) that I can access as Float32Arrays by calling AudioBuffer.prototype.getChannelData(). MDN also says:
The buffer contains data in the following format: non-interleaved IEEE754 32-bit linear PCM with a nominal range between -1 and +1, that is, 32bits floating point buffer, with each samples between -1.0 and 1.0.
The point is to find what I have to extract from the incoming Buffers and how I should transform it so it's suitable for the Float32Array destination (and remains valid wave data), knowing that the audio source is stereo and the AudioBuffer isn't.
My best contender so far was the Buffer.prototype.readFloatLE() method whose name looks like it would solve my problem, but this wasn't a success (just noise).
My first try (before doing research) was just to naively copy buffer data to Float32Array and interleaving indexes to handle stereo/mono conversion. Obviously it mostly produced noise but I could hear some of the sound I recorded (incredibly distorted but surely present) so I guess I should mention that.
This is a simplified version of my naive try (I'm aware this is not meant to work well, I just include it in my question as a base of discussion):
import { AudioBuffer } from 'web-audio-api'
import Microphone from 'node-microphone'
const rate = 44100
const channels = 2 // Number of source channels
const microphone = new Microphone({ // These parameters result to the arecord command above
channels,
rate,
device: 'hw:1,0',
bitwidth: 16,
endian: 'little',
encoding: 'signed-integer'
})
const audioBuffer = new AudioBuffer(
1, // 1 channel
30 * rate, // 30 seconds buffer
rate
})
const chunks = []
const data = audioBuffer.getChannelData(0) // This is the Float32Array
const stream = microphone.startRecording()
setTimeout(() => microphone.stopRecording(), 5000) // Recording for 5 seconds
stream.on('data', chunk => chunks.push(chunk))
stream.on('close', () => {
chunks.reduce((offset, chunk) => {
for (var index = 0; index < chunk.length; index += channels) {
let value = 0
for (var channel = 0; channel < channels; channel++) {
value += chunk[index + channel]
}
data[(offset + index) / channels] = value / channels // Average value from the two channels
}
return offset + chunk.length // Since data comes as chunks, this offsets AudioBuffer's index
}, 0)
})
I would be really grateful if you could help :)
So the input stereo signal is coming as 16 bits signed integers, interleaving left and right channels, meaning that the corresponding buffers (8 bits unsigned integers) have this format for a single stereo sample:
[LEFT ] 8 bits (LSB)
[LEFT ] 8 bits (MSB)
[RIGHT] 8 bits (LSB)
[RIGHT] 8 bits (MSB)
Since arecord is configured with little endian format, the Least Significant Byte (LSB) comes first, and the Most Significant Byte (MSB) comes next.
The AudioBuffer single channel buffer, represented by a Float32Array, expects values between -1 and 1 (one value per sample).
So to map values from the input Buffer to the destination Float32Array, I had to use the Buffer.prototype.readInt16LE(offset) method incrementing the bytes offset parameter by 4 each sample (2 left bytes + 2 right bytes = 4 bytes), and interpolating input values from range [-32768;+32768] (16 bits signed integer range) to range [-1;+1]:
import { AudioBuffer } from 'web-audio-api'
import Microphone from 'node-microphone'
const rate = 44100
const channels = 2 // 2 input channels
const microphone = new Microphone({
channels,
rate,
device: 'hw:1,0',
bitwidth: 16,
endian: 'little',
encoding: 'signed-integer'
})
const audioBuffer = new AudioBuffer(
1, // 1 channel
30 * rate, // 30 seconds buffer
rate
})
const chunks = []
const data = audioBuffer.getChannelData(0)
const stream = microphone.startRecording()
setTimeout(() => microphone.stopRecording(), 5000) // Recording for 5 seconds
stream.on('data', chunk => chunks.push(chunk))
stream.on('close', () => {
chunks.reduce((offset, chunk) => {
for (var index = 0; index < chunk.length; index += channels + 2) {
let value = 0
for (var channel = 0; channel < channels; channel++) {
// Iterates through input channels and adds the values
// of all the channel so we can compute the
// average value later to reduce them into a mono signal
// Multiplies the channel index by 2 because
// there are 2 bytes per channel sample
value += chunk.readInt16LE(index + channel * 2)
}
// Interpolates index according to the number of input channels
// (also divides it by 2 because there are 2 bytes per channel sample)
// and computes average value as well as the interpolation
// from range [-32768;+32768] to range [-1;+1]
data[(offset + index) / channels / 2] = value / channels / 32768
}
return offset + chunk.length
}, 0)
})

javascript: smallest JSON.stringify for Float32Array?

FireFox 46.0.1: I am using 3rd-party (easyrtc) software to send 15KB chunks of Float32Arrays between peers. Easyrtc insists that the data be JSON-able. Unfortunately, JSON.stringify yields a string more than twice as long as the original data: 16384 bytes of data becomes a string of length 35755. Below is my test code followed by the console output. What if anything can I do to reduce the stringify'd size? Is there a way to send the values only (no keys)? Can I use the 'replacer' argument to send only the values, and if so, don't I need to use a replacer on the corresponding JSON.parse on the receiving end?
var g_testBufferNBytes = 4096 * 4;
var g_testBuffer = new ArrayBuffer(g_testBufferNBytes);
var g_testBufferView = new Float32Array(g_testBuffer);
console.log("array byte length " + g_testBuffer.byteLength);
console.log("view byte length " + g_testBufferView.byteLength);
var j = JSON.stringify(g_testBufferView);
console.log("j length " + j.length);
var newBuf = JSON.parse(j);
console.log("newBuf length " + Object.keys(newBuf).length);
CONSOLE:
array byte length 16384
view byte length 16384
j length 35755
newBuf length 4096
Yes
ES6: Assume that your data are in let f32 = g_testBufferView (array Float32Array) ) - whe can save it as JSON array in at leas 4 ways:
// code
let f32json = JSON.stringify(f32);
let f32jsonArr = JSON.stringify(Array.from(f32));
let f32base64 = btoa(String.fromCharCode(...(new Uint8Array(f32.buffer))));
let f32base128 = ... // not trivial, look below
// decode
let df32json = new Float32Array(Object.values(JSON.parse(f32json)));
let df32jsonArr = new Float32Array(JSON.parse(f32jsonArr));
let df32base64 = new Float32Array(new Uint8Array([...atob(f32base64)].map(c => c.charCodeAt(0))).buffer);
let df32base128 = ... // not trivial, look below
Note that Object.values return values sorted by numeric keys (look here).
Here is working example. You can also use base128 do decode but I not use in this example (to not complicate it) - more details here.
If your Float32Array- f32 has 4096 elements equals to 0.3 then:
f32 has 16384 bytes,
f32json (j from your question) has 109483 bytes (which is >6x bigger than f32)
f32jsonArr has 81921 bytes (which is >5x bigger than f32)
f32base64 has 21848 bytes(which is ~1.3x bigger than f32)
f32base128 has 18725 bytes (whis is <1.15x bigger than f32) but chrome will send ~2x bigger request (depends on input data)
If your Float32Array- f32 has 4096 elements equals integer from 1 to 9 then:
f32 has 16384 bytes - CONST,
f32json (j from your question) has 35755 bytes (which is >2x bigger than f32)
f32jsonArr has 8193 bytes (which is 2x SMALLER (sic!) than f32)
f32base64 has 21848 bytes - CONST (which is ~1.3x bigger than f32)
f32base128 has 18725 bytes - CONST (whis is <1.15x bigger than f32) but chrome will send ~2x bigger request (depends on input data)
Conclusion
The smallest result which not depends of array values (result size is constant) we get for f32base64 ~33% bigger than input array size. For f32base128 - it contains valid JSON (string) which is something about <15% bigger than input, but chrome during sending increase this size (look here - on 'update' section). So use f32base64 - this is probably the smallest JSON that you can get without more sophisticated methods.

Javascript Convert int value to octet stream Array

I want convert an integer (signed) to 32 bit (big endian) into a octet stream and give the octet stream as a array value to the constructor of a
Buffer Object.
I can create it in the console for example for the value -2000:
<code>
buf = Buffer(4)
buf.writeInt32BE(-2000)
buf // is <Buffer ff ff f8 30>
buf1 = new Buffer([0xff, 0xff, 0xf8, 0x30])
</code>
The value -3000 is for example -3000 : 0xff ,0xff, 0xf4, 0x48
But the framework i use accepts not the writeInt32BE function and throws exception.
How can i convert a 32 bit integer value signed to a octet Array stream without the writeInt32BE ?
A function that takes a value and returns an array of octet stream.
Using a 4 byte array buffer, converted to a data view and calling setInt32 on the view seems to work. This approach supports specification of both little endian and big endian (the default) formats independent of machine architecture.
function bigEnd32( value) {
var buf = new ArrayBuffer(4);
var view = new DataView(buf);
view.setInt32( 0, value);
return view;
}
// quick test (in a browser)
var n = prompt("Signed 32: ");
var view = bigEnd32( +n);
for(var i = 0 ; i < 4; ++i)
console.log(view.getUint8( i));
Documentation was located searching for "MDN ArrayBuffer" "MDN Dataview" etc. Check out DataView in detail for properties that access the underlying array buffer - you may be able to tweak the code to suite your application.

What does it mean if binary data is "4 byte single format" and how do I read it in JavaScript?

I have to read a binary file which is said to be encoded 4 byte single format and never having to work with binary data, I don't know what this means.
I can do this reading a file with binary data in JavaScript:
d = new FileReader();
d.onload = function (e) {
var i, len;
// grab a "chunk"
response_buffer = e.target.result.slice(0, 1024);
view = new DataView(response_buffer);
for (i = 0, len = response_buffer.byteLength; i < len; i += 1) {
// hmhm
console.log(view.getUint8(i));
}
}
d.readAsArrayBuffer(some_file);
Which runs a loop from 0 to 1023 and I am getting numbers on the console, but I don't know if this is my decoded data :-)
Question:
What is 4 byte single format and how do I access the data correctly? What is the difference between say getUint8() and getint8() or getInt32() in "human understandable language"?
Thanks!
4 byte single format is not a commonly understood term in computer science.
If you could expect your file to be a series of single precision floating point numbers, then I might guess that "4 byte single format" means single precision floating point because each of those is four bytes long.
You will want to use getFloat32() to parse single precision floating point numbers from the binary stream.
If you want 1024 numbers parsed with getFloat32(), then you need 1024*4 bytes and you need to advance your for loop by four bytes each time since getFloat32() processes four bytes at a time:
d = new FileReader();
d.onload = function (e) {
var i, len;
// grab a "chunk"
response_buffer = e.target.result.slice(0, 1024 * 4);
view = new DataView(response_buffer);
for (i = 0, len = response_buffer.byteLength; i < len; i += 4) {
// hmhm
console.log(view.getFloat32(i));
}
}
d.readAsArrayBuffer(some_file);
Also, please note that IE10 and IOS 5 do not have the .slice() method for an ArrayBuffer if you're planning on using this in a general web page.

Crypto - Is SJCL (javascript) encryption compatible with OpenSSL?

I am trying to decrypt some information that has been encrypted with the SJCL (Stanford Javascript Crypto Library). An example page is at http://bitwiseshiftleft.github.io/sjcl/demo/.
If I encrypt some data, I have been unable to decrypt it using OpenSSL (version 1.0.1f). I have seen another question on stackoverflow asking about this - but that question and its answers didn't really help.
For instance, encrypting with a password of 'password', and a random salt of '6515636B 82C5AC56' with 10000 iterations of a 256 bit key size gives a Key of 'D8CCAA75 3E2983F0 3657AB3C 8A68A85A 9E9F1CAC 43DAB645 489CDE58 0A9EBDAE', which is exactly what I get with OpenSSL. So far, so good.
When I use SJCL with this key and an IV of '9F62544C 9D3FCAB2 DD0833DF 21CA80CF' to encrypt, say, the message 'mymessage', then I get the ciphertext:
{"iv":"n2JUTJ0/yrLdCDPfIcqAzw==",
"v":1,
"iter":10000,
"ks":256,
"ts":64,
"mode":"ccm",
"adata":"",
"cipher":"aes",
"salt":"ZRVja4LFrFY=",
"ct":"FCuQWGYz3onE/lRt/7vCl5A="}
However, no matter how I modify or rewrite my OpenSSL C++ code, I cannot decrypt this data.
I've googled and found a few code samples, but nothing that has actually worked.
I'm aware that I need to use the CCM cipher mode in OpenSSL - but this mode is poorly documented.
Can anyone post some OpenSSL code to successfully decrypt this data?
You can copy-paste the example at http://wiki.openssl.org/index.php/EVP_Authenticated_Encryption_and_Decryption with a few changes.
First, you need to Base64 decode SJCL's data. But you knew that.
Second, you need to split the message into ct and tag. In this case, ct is the first 9 bytes, and tag is the 8 bytes starting at ct[9].
Third, you need to set the tag length to ts/8 = 8, and you need to set the IV length correctly. If you set the IV too long in SJCL, it will truncate it to 15 - LOL (length of length), where LOL is between 2 and 4 (because SJCL enforces <2^32 bytes length), and is the number of bytes required to describe the length of the message. This is 2 unless the message is at least 65536 bytes long, in which case it is 3, unless the message is at least 2^24 bytes long, in which case it is 4. Keep in mind that if you're decrypting, the ciphertext you are passed includes the tag but LOL must be computed from the message length, which does not include the tag.
With those changes, it should work:
#include <openssl/evp.h>
void handleErrors() {
abort();
}
int decryptccm(unsigned char *ciphertext, int ciphertext_len, unsigned char *aad,
int aad_len, unsigned char *tag, unsigned char *key, unsigned char *iv,
unsigned char *plaintext)
{
EVP_CIPHER_CTX *ctx;
int len;
int plaintext_len;
int ret;
/* Create and initialise the context */
if(!(ctx = EVP_CIPHER_CTX_new())) handleErrors();
/* Initialise the decryption operation. */
if(1 != EVP_DecryptInit_ex(ctx, EVP_aes_256_ccm(), NULL, NULL, NULL))
handleErrors();
int lol = 2;
if (ciphertext_len >= 1<<16) lol++;
if (ciphertext_len >= 1<<24) lol++;
if(1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_IVLEN, 15-lol, NULL))
handleErrors();
/* Set expected tag value. */
if(1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_TAG, 8, tag))
handleErrors();
/* Initialise key and IV */
if(1 != EVP_DecryptInit_ex(ctx, NULL, NULL, key, iv)) handleErrors();
/* Provide the total ciphertext length
*/
if(1 != EVP_DecryptUpdate(ctx, NULL, &len, NULL, ciphertext_len))
handleErrors();
/* Provide any AAD data. This can be called zero or more times as
* required
*/
if(1 != EVP_DecryptUpdate(ctx, NULL, &len, aad, aad_len))
handleErrors();
/* Provide the message to be decrypted, and obtain the plaintext output.
* EVP_DecryptUpdate can be called multiple times if necessary
*/
ret = EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len);
plaintext_len = len;
/* Clean up */
EVP_CIPHER_CTX_free(ctx);
if(ret > 0)
{
/* Success */
return plaintext_len;
}
else
{
/* Verify failed */
return -1;
}
}
int main(int argc, char **argv) {
// base64-decoded from your example
unsigned char iv[] = { 0x9F,0x62,0x54,0x4C,0x9D,0x3F,0xCA,0xB2,0xDD,0x08,0x33,0xDF,0x21,0xCA,0x80,0xCF };
unsigned char ct[] = { 0x14,0x2B,0x90,0x58,0x66,0x33,0xDE,0x89,0xC4,0xFE,0x54,0x6D,0xFF,0xBB,0xC2,0x97,0x90 };
unsigned char ky[] = { 0xD8,0xCC,0xAA,0x75 ,0x3E,0x29,0x83,0xF0 ,0x36,0x57,0xAB,0x3C ,0x8A,0x68,0xA8,0x5A ,0x9E,0x9F,0x1C,0xAC ,0x43,0xDA,0xB6,0x45 ,0x48,0x9C,0xDE,0x58 ,0x0A,0x9E,0xBD,0xAE };
const unsigned char *message = (const unsigned char *)"mymessage";
unsigned char plaintext[1000];
int ret = decryptccm(ct, 9, "", 0, &ct[9], ky, iv, plaintext);
plaintext[9] = 0;
printf("%d,%s\n",ret,plaintext);
return 0;
}
This program returns "9,mymessage" on my machine.

Categories