Spaces:
Sleeping
Sleeping
File size: 6,281 Bytes
6655e35 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 |
#define NAPI_EXPERIMENTAL
#include <node_api.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <limits.h>
#define BIT_MASK(n) (~( ((~0ull) << ((n)-1)) << 1 ))
// The maximum size we'll store on the stack. If we need a larger temporary
// buffer malloc will be called.
#define BUFFER_STACK_SIZE 32
#if defined(_WIN16) || defined(_WIN32) || defined(_WIN64)
#define bswap64(x) _byteswap_uint64(x)
#else
#define bswap64(x) __builtin_bswap64(x)
#endif
/**
* Converts a Buffer to bigint.
* node param 0: buffer
* node param 1: big_endian (optional boolean)
*
* returns bigint
*/
napi_value toBigInt (napi_env env, napi_callback_info info) {
napi_value argv[2];
napi_status status;
size_t argc = 2;
status = napi_get_cb_info(env, info, &argc, argv, NULL, NULL);
assert(status == napi_ok);
if (argc < 1) {
napi_throw_error(env, "EINVAL", "Too few arguments");
return NULL;
}
bool big_endian;
status = napi_get_value_bool(env, argv[1], &big_endian);
if (status == napi_boolean_expected) { big_endian = false; }
uint8_t* buffer;
size_t len;
status = napi_get_buffer_info(env, argv[0], (void**) &buffer, &len);
assert(status == napi_ok);
// If len is not divisible by 8 bytes, we'll need to copy
bool not_64_aligned = (len & 7) != 0;
size_t overflow_len = not_64_aligned ? 8 - (len & 0x7) : 0;
// Buffer is managed by VM, so copy it out (TODO: perhaps we can increase refcount?)
size_t aligned_len = len + overflow_len;
size_t len_in_words = not_64_aligned ? (len >> 3) + 1 : (len >> 3);
bool fits_in_stack = aligned_len <= BUFFER_STACK_SIZE;
uint8_t copy[BUFFER_STACK_SIZE];
uint8_t* bufTemp = fits_in_stack ? copy : malloc(aligned_len);
if (overflow_len > 0) {
memset(bufTemp + len, 0, overflow_len);
}
memcpy(bufTemp, buffer, len);
uint64_t* as_64_aligned = (uint64_t*) bufTemp;
size_t overflow_in_bits = overflow_len << 3; // == overflow_len * 8
napi_value out;
// swap
if (big_endian) {
if (len_in_words == 1) {
as_64_aligned[0] = not_64_aligned ? bswap64(as_64_aligned[0]) >> overflow_in_bits : bswap64(as_64_aligned[0]);
} else {
uint64_t temp;
size_t last_word = len_in_words - 1;
size_t end_ptr = last_word;
int32_t offset;
for (offset = 0; offset < (int32_t)(len_in_words / 2); offset++) {
temp = as_64_aligned[offset];
as_64_aligned[offset] = as_64_aligned[end_ptr];
as_64_aligned[end_ptr] = temp;
end_ptr--;
}
uint64_t prev_overflow = 0;
for (offset = last_word; offset >= 0; offset--) {
uint64_t as_little_endian = bswap64(as_64_aligned[offset]);
uint64_t overflow = as_little_endian & BIT_MASK(overflow_in_bits);
as_64_aligned[offset] = not_64_aligned ? (as_little_endian >> overflow_in_bits) | prev_overflow : as_little_endian;
prev_overflow = overflow << (64 - overflow_in_bits);
}
}
}
status = napi_create_bigint_words(env, 0, len_in_words, as_64_aligned , &out);
assert(status == napi_ok);
if (!fits_in_stack) {
free(bufTemp);
}
return out;
}
/**
* Converts a BigInt to a Buffer
* node param 0: BigInt
* node param 1: buffer
* node param 2: big_endian (optional boolean)
*
* returns bigint
*/
napi_value fromBigInt (napi_env env, napi_callback_info info) {
napi_value argv[3];
napi_status status;
size_t argc = 3;
status = napi_get_cb_info(env, info, &argc, argv, NULL, NULL);
assert(status == napi_ok);
if (argc < 1) {
napi_throw_error(env, "EINVAL", "Too few arguments");
return NULL;
}
size_t byte_width;
bool big_endian;
status = napi_get_value_bool(env, argv[2], &big_endian);
if (status == napi_boolean_expected) { big_endian = false; }
size_t word_count;
status = napi_get_value_bigint_words(env, argv[0], NULL, &word_count, NULL);
assert(status == napi_ok);
uint8_t* raw_buffer;
status = napi_get_buffer_info(env, argv[1], (void**) &raw_buffer, &byte_width);
assert(status == napi_ok);
if (word_count == 0) {
memset(raw_buffer, 0, byte_width);
return argv[1];
}
int sign_bit = 0;
bool not_64_aligned = (byte_width & 7) != 0;
size_t overflow_len = not_64_aligned ? 8 - (byte_width & 0x7) : 0;
size_t word_width = (byte_width >> 3) + (not_64_aligned ? 1 : 0);
size_t original_word_width = word_width;
if (word_count > word_width) {
word_count = word_width;
}
size_t word_width_bytes = (word_count << 3);
bool fits_in_stack = word_width_bytes <= BUFFER_STACK_SIZE;
uint64_t* conv_buffer = (uint64_t*) raw_buffer;
uint64_t stack_buffer[BUFFER_STACK_SIZE];
if (not_64_aligned) {
conv_buffer = fits_in_stack ? stack_buffer : malloc(byte_width + overflow_len);
}
memset(conv_buffer, 0, byte_width + overflow_len);
status = napi_get_value_bigint_words(env, argv[0], &sign_bit, &word_count, conv_buffer);
assert(status == napi_ok);
if (big_endian) {
uint64_t temp;
size_t conv_words = original_word_width;
size_t last_word = conv_words - 1;
size_t end_ptr = last_word;
int32_t offset;
for (offset = 0; offset < (int32_t)(conv_words / 2); offset++) {
temp = bswap64(conv_buffer[offset]);
conv_buffer[offset] = bswap64(conv_buffer[end_ptr]);
conv_buffer[end_ptr] = temp;
end_ptr--;
}
if (conv_words & 1) {
conv_buffer[conv_words / 2] = bswap64(conv_buffer[conv_words / 2]);;
}
}
if (not_64_aligned) {
memcpy(raw_buffer, big_endian ? (uint64_t*)(((uint8_t*)conv_buffer) + (8-(byte_width & 7))) : conv_buffer, byte_width);
if (!fits_in_stack) {
free(conv_buffer);
}
}
return argv[1];
}
napi_value init_all (napi_env env, napi_value exports) {
napi_value bigint_fn;
napi_value frombigint_fn;
napi_create_function(env, NULL, 0, toBigInt, NULL, &bigint_fn);
napi_create_function(env, NULL, 0, fromBigInt, NULL, &frombigint_fn);
napi_set_named_property(env, exports, "toBigInt", bigint_fn);
napi_set_named_property(env, exports, "fromBigInt", frombigint_fn);
return exports;
}
NAPI_MODULE(NODE_GYP_MODULE_NAME, init_all); |