Skip to content

Commit

Permalink
64-bit methods on Pointer now use strings
Browse files Browse the repository at this point in the history
  • Loading branch information
rbranson committed Nov 18, 2010
1 parent 9ddbdf3 commit 02b5f52
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 36 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ In addition to the basic types, there are type aliases for common C types.
longlong long long
ulonglong unsigned long long

# V8 and 64-bit Types

Internally, V8 stores integers that will fit into a 32-bit space in a 32-bit integer, and those that fall outside of this get put into double-precision floating point numbers. This is problematic because FP numbers are imprecise. To get around this, the methods in node-ffi that deal with 64-bit integers return strings and can accept strings as parameters.

# LICENSE

See LICENSE file.
6 changes: 6 additions & 0 deletions src/ffi.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <string.h>
#include <dlfcn.h>
#include <limits.h>
#include <errno.h>
#define __STDC_LIMIT_MACROS true
#include <stdint.h>
#include <sys/mman.h>
Expand All @@ -19,13 +20,18 @@
#include <pthread.h>
#include <queue>

#define INTEGER_CONVERSION_BUFFER_SIZE 64

#define UINT8_MIN 0
#define UINT16_MIN 0
#define UINT32_MIN 0
#define UINT64_MIN 0

#define THROW_ERROR_EXCEPTION(x) ThrowException(Exception::Error(String::New(x)))

#define STR_TO_INT64(x) strtoll(x, NULL, 0)
#define STR_TO_UINT64(x) strtoull(x, NULL, 0)

using namespace v8;
using namespace node;

Expand Down
90 changes: 59 additions & 31 deletions src/pointer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -174,8 +174,8 @@ Handle<Value> Pointer::PutUInt8(const Arguments& args)
int64_t val = args[0]->IntegerValue();

if (val >= UINT8_MIN && val <= UINT8_MAX) {
uint8_t cvt = (uint8_t)val;
memcpy(ptr, &cvt, sizeof(uint8_t));
uint8_t cvt = (uint8_t)val;
memcpy(ptr, &cvt, sizeof(uint8_t));
}
else {
return THROW_ERROR_EXCEPTION("Value out of Range.");
Expand Down Expand Up @@ -251,8 +251,8 @@ Handle<Value> Pointer::PutInt16(const Arguments& args)
int64_t val = args[0]->IntegerValue();

if (val >= INT16_MIN && val <= INT16_MAX) {
int16_t cvt = (int16_t)val;
memcpy(ptr, &cvt, sizeof(int16_t));
int16_t cvt = (int16_t)val;
memcpy(ptr, &cvt, sizeof(int16_t));
}
else {
return THROW_ERROR_EXCEPTION("Value out of Range.");
Expand Down Expand Up @@ -290,8 +290,8 @@ Handle<Value> Pointer::PutUInt16(const Arguments& args)
int64_t val = args[0]->IntegerValue();

if (val >= UINT16_MIN && val <= UINT16_MAX) {
uint16_t cvt = (uint16_t)val;
memcpy(ptr, &cvt, sizeof(uint16_t));
uint16_t cvt = (uint16_t)val;
memcpy(ptr, &cvt, sizeof(uint16_t));
}
else {
return THROW_ERROR_EXCEPTION("Value out of Range.");
Expand Down Expand Up @@ -328,7 +328,7 @@ Handle<Value> Pointer::PutInt32(const Arguments& args)
int64_t val = args[0]->IntegerValue();

if (val >= INT32_MIN && val <= INT32_MAX) { // XXX: Will this ever be false?
memcpy(ptr, &val, sizeof(int32_t));
memcpy(ptr, &val, sizeof(int32_t));
}
else {
return THROW_ERROR_EXCEPTION("Value out of Range.");
Expand Down Expand Up @@ -365,7 +365,7 @@ Handle<Value> Pointer::PutUInt32(const Arguments& args)
int64_t val = args[0]->IntegerValue();

if (val >= UINT32_MIN && val <= UINT32_MAX) { // XXX: Will this ever be false?
memcpy(ptr, &val, sizeof(uint32_t));
memcpy(ptr, &val, sizeof(uint32_t));
}
else {
return THROW_ERROR_EXCEPTION("Value out of Range.");
Expand Down Expand Up @@ -398,14 +398,27 @@ Handle<Value> Pointer::PutInt64(const Arguments& args)
Pointer *self = ObjectWrap::Unwrap<Pointer>(args.This());
unsigned char *ptr = self->GetPointer();

if (args.Length() >= 1 && args[0]->IsNumber()) {
int64_t val = args[0]->IntegerValue();

if (val >= INT64_MIN && val <= INT64_MAX) {
memcpy(ptr, &val, sizeof(int64_t));
}
else {
return THROW_ERROR_EXCEPTION("Value out of Range.");
// Have to do this because strtoll doesn't set errno to 0 on success :(
errno = 0;

if (args.Length() >= 1) {
if (args[0]->IsNumber() || args[0]->IsString()) {
int64_t val;

if (args[0]->IsNumber()) {
val = args[0]->IntegerValue();
}
else { // assumed args[0]->IsString() from condition above
String::Utf8Value str(args[0]->ToString());
val = STR_TO_INT64(*str);
}

if (errno != ERANGE && (val >= INT64_MIN && val <= INT64_MAX)) {
memcpy(ptr, &val, sizeof(int64_t));
}
else {
return THROW_ERROR_EXCEPTION("Value out of Range.");
}
}
}
if (args.Length() == 2 && args[1]->IsBoolean() && args[1]->BooleanValue()) {
Expand All @@ -421,13 +434,16 @@ Handle<Value> Pointer::GetInt64(const Arguments& args)
Pointer *self = ObjectWrap::Unwrap<Pointer>(args.This());
unsigned char *ptr = self->GetPointer();
int64_t val = *((int64_t *)ptr);

char buf[INTEGER_CONVERSION_BUFFER_SIZE];

bzero(buf, INTEGER_CONVERSION_BUFFER_SIZE);
snprintf(buf, INTEGER_CONVERSION_BUFFER_SIZE, "%lld", val);

if (args.Length() == 1 && args[0]->IsBoolean() && args[0]->BooleanValue()) {
self->MovePointer(sizeof(int64_t));
}

// TODO: A way for V8 to take this int64_t as what it should be?
return scope.Close(Number::New(val));
return scope.Close(String::New(buf));
}

Handle<Value> Pointer::PutUInt64(const Arguments& args)
Expand All @@ -436,14 +452,23 @@ Handle<Value> Pointer::PutUInt64(const Arguments& args)
Pointer *self = ObjectWrap::Unwrap<Pointer>(args.This());
unsigned char *ptr = self->GetPointer();

if (args.Length() >= 1 && args[0]->IsNumber()) {
int64_t val = args[0]->IntegerValue(); // JavaScript can't theoretically support UINT64

if (val >= UINT64_MIN && val <= INT64_MAX) {
memcpy(ptr, &val, sizeof(int64_t));
}
else {
return THROW_ERROR_EXCEPTION("Value out of Range.");
// Have to do this because strtoull doesn't set errno to 0 on success :(
errno = 0;

if (args.Length() >= 1) {
if (args[0]->IsNumber() || args[0]->IsString()) {
uint64_t val;

// Convert everything to a string because it's easier this way
String::Utf8Value str(args[0]->ToString());
val = STR_TO_UINT64(*str);

if ((*str)[0] != '-' && errno != ERANGE && (val >= UINT64_MIN && val <= UINT64_MAX)) {
memcpy(ptr, &val, sizeof(uint64_t));
}
else {
return THROW_ERROR_EXCEPTION("Value out of Range.");
}
}
}
if (args.Length() == 2 && args[1]->IsBoolean() && args[1]->BooleanValue()) {
Expand All @@ -459,13 +484,16 @@ Handle<Value> Pointer::GetUInt64(const Arguments& args)
Pointer *self = ObjectWrap::Unwrap<Pointer>(args.This());
unsigned char *ptr = self->GetPointer();
uint64_t val = *((uint64_t *)ptr);

char buf[INTEGER_CONVERSION_BUFFER_SIZE];

bzero(buf, INTEGER_CONVERSION_BUFFER_SIZE);
snprintf(buf, INTEGER_CONVERSION_BUFFER_SIZE, "%llu", val);

if (args.Length() == 1 && args[0]->IsBoolean() && args[0]->BooleanValue()) {
self->MovePointer(sizeof(uint64_t));
}

// TODO: A way for V8 to take this int64_t as what it should be?
return scope.Close(Number::New(val));

return scope.Close(String::New(buf));
}

Handle<Value> Pointer::PutFloat(const Arguments& args)
Expand Down
30 changes: 25 additions & 5 deletions test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,17 +66,37 @@ assert.equal(Math.pow(2, 32) - 1, ptr.getUInt32());
assert.notEqual(Math.pow(2, 64).toString(), "18446744073709551616");
assert.notEqual(Math.pow(2, 63).toString(), "9223372036854775808");

assert.throws(function() { ptr.putInt64(-9223372036854785808); });
assert.throws(function() { ptr.putInt64(9223372036854785808); });
//assert.throws(function() { ptr.putInt64(-9223372036854785808); });
//assert.throws(function() { ptr.putInt64(9223372036854785808); });

ptr.putInt64(0 - Math.pow(2, 63));
assert.equal(0 - Math.pow(2, 63), ptr.getInt64());

assert.throws(function() { ptr.putUInt64(-1); });
assert.throws(function() { ptr.putUInt64(9223372036854785808); });
assert.throws(function() { ptr.putUInt64(18446744073709551616); });

ptr.putUInt64(Math.pow(2, 64));
assert.equal(Math.pow(2, 64), ptr.getUInt64());
ptr.putUInt64(Math.pow(2, 63) - 10000);
assert.equal(Math.pow(2, 63) - 10000, ptr.getUInt64());

// check for string support
assert.throws(function() { ptr.putInt64("9223372036854775808"); });
assert.throws(function() { ptr.putInt64("-9223372036854775809"); });

// allows INT64_MAX value
ptr.putInt64("9223372036854775807");
assert.equal("9223372036854775807", ptr.getInt64());

// allows INT64_MIN value
ptr.putInt64("-9223372036854775808");
assert.equal("-9223372036854775808", ptr.getInt64());

// Uint should throw error on value > UINT64_MAX or negative number
assert.throws(function() { ptr.putUInt64("18446744073709551616"); });
assert.throws(function() { ptr.putUInt64("-1"); });

// allows UINT64_MAX value
ptr.putUInt64("18446744073709551615");
assert.equal("18446744073709551615", ptr.getUInt64());

////////////////

Expand Down

0 comments on commit 02b5f52

Please sign in to comment.