Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement 'pack(n)' option #34

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 28 additions & 9 deletions lib/struct.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,10 +122,14 @@ function Struct () {
StructType.size = 0
StructType.alignment = 0
StructType.indirection = 1
StructType.isPacked = opt.packed ? Boolean(opt.packed) : false
StructType.pack = typeof(opt.pack) === "number" ? opt.pack : opt.packed ? 1 : 0;
StructType.get = get
StructType.set = set

if( StructType.pack ) {
assert([1,2,4,8,16].indexOf(StructType.pack)>=0,'pack value must be 1, 2, 4, 8, or 16')
}

// Read the fields list and apply all the fields to the struct
// TODO: Better arg handling... (maybe look at ES6 binary data API?)
var arg = arguments[0]
Expand Down Expand Up @@ -247,13 +251,17 @@ function recalc (struct) {
if (type.indirection > 1) {
alignment = ref.alignof.pointer
}
if (struct.isPacked) {
struct.alignment = Math.min(struct.alignment || alignment, alignment)
} else {
struct.alignment = Math.max(struct.alignment, alignment)
}
// the alignment of the structure will be as that of its largest member
struct.alignment = Math.max(struct.alignment, alignment)
})

if( struct.pack ) {
// if packed, the alignment of the structure will be that of the n-byte
// boundary or the alignment of the largest member of the structure; whichever
// is smaller
struct.alignment = Math.min(struct.alignment, struct.pack);
}

// second loop through sets the `offset` property on each "field"
// object, and sets the `struct.size` as we go along
fieldNames.forEach(function (name) {
Expand All @@ -276,12 +284,19 @@ function recalc (struct) {
function addType (type) {
var offset = struct.size
var align = type.indirection === 1 ? type.alignment : ref.alignof.pointer
var padding = struct.isPacked ? 0 : (align - (offset % align)) % align
if( struct.pack ) {
// #pragma pack(n)
// https://docs.microsoft.com/en-us/cpp/preprocessor/pack?view=vs-2017
// The alignment of a member will be on a boundary that is either a multiple
// of n or a multiple of the size of the member, whichever is smaller
align = Math.min(struct.pack, align);
}
var padding = (align - (offset % align)) % align
var size = type.indirection === 1 ? type.size : ref.sizeof.pointer

offset += padding

if (!struct.isPacked) {
if (!struct.pack) {
assert.equal(offset % align, 0, "offset should align")
}

Expand All @@ -292,7 +307,11 @@ function recalc (struct) {
return offset
}

// any final padding?
// http://www.catb.org/esr/structure-packing/#_structure_alignment_and_padding
// The stride address of a structure is the first address following the structure
// data that has the same alignment as the structure. The general rule of
// trailing structure padding is that the structure has trailing padding out to
// its stride address
var left = struct.size % struct.alignment
if (left > 0) {
debug('additional padding to the end of struct:', struct.alignment - left)
Expand Down
17 changes: 17 additions & 0 deletions test/struct.js
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,23 @@ describe('Struct', function () {
test19.defineProperty('next', ref.refType(test19));
test(test19, 19);

var test20 = Struct({
a: 'char',
p: ref.refType('void')
}, {packed: true})
test(test20, 20)

var test21 = Struct({
a: 'char',
p: ref.refType('void')
})
test(test21, 21)

var test22 = Struct({
a: 'char',
p: ref.refType('void')
},{pack: 2})
test(test22, 22)
})

describe('packed struct', function () {
Expand Down
15 changes: 15 additions & 0 deletions test/struct_tests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,12 @@ typedef struct _test21 {
void *p;
} test21;

#pragma pack(2)
typedef struct _test22 {
char a;
void *p;
} test22;

void Initialize(v8::Handle<v8::Object> target) {
Nan::HandleScope scope;

Expand Down Expand Up @@ -289,9 +295,18 @@ void Initialize(v8::Handle<v8::Object> target) {

target->Set(Nan::New<v8::String>("test20 sizeof").ToLocalChecked(), Nan::New<v8::Number>(sizeof(test20)));
target->Set(Nan::New<v8::String>("test20 alignof").ToLocalChecked(), Nan::New<v8::Number>(__alignof__(test20)));
target->Set(Nan::New<v8::String>("test20 offsetof a").ToLocalChecked(), Nan::New<v8::Number>(offsetof(test20, a)));
target->Set(Nan::New<v8::String>("test20 offsetof p").ToLocalChecked(), Nan::New<v8::Number>(offsetof(test20, p)));

target->Set(Nan::New<v8::String>("test21 sizeof").ToLocalChecked(), Nan::New<v8::Number>(sizeof(test21)));
target->Set(Nan::New<v8::String>("test21 alignof").ToLocalChecked(), Nan::New<v8::Number>(__alignof__(test21)));
target->Set(Nan::New<v8::String>("test21 offsetof a").ToLocalChecked(), Nan::New<v8::Number>(offsetof(test21, a)));
target->Set(Nan::New<v8::String>("test21 offsetof p").ToLocalChecked(), Nan::New<v8::Number>(offsetof(test21, p)));

target->Set(Nan::New<v8::String>("test22 sizeof").ToLocalChecked(), Nan::New<v8::Number>(sizeof(test22)));
target->Set(Nan::New<v8::String>("test22 alignof").ToLocalChecked(), Nan::New<v8::Number>(__alignof__(test22)));
target->Set(Nan::New<v8::String>("test22 offsetof a").ToLocalChecked(), Nan::New<v8::Number>(offsetof(test22, a)));
target->Set(Nan::New<v8::String>("test22 offsetof p").ToLocalChecked(), Nan::New<v8::Number>(offsetof(test22, p)));
}

} // anonymous namespace
Expand Down