yencode 1.0.8 → 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +339 -231
- package/binding.gyp +292 -39
- package/crcutil-1.0/code/multiword_64_64_gcc_amd64_asm.cc +7 -7
- package/crcutil-1.0/code/multiword_64_64_gcc_i386_mmx.cc +14 -14
- package/crcutil-1.0/code/multiword_64_64_intrinsic_i386_mmx.cc +1 -1
- package/crcutil-1.0/code/uint128_sse2.h +2 -0
- package/index.js +329 -22
- package/package.json +2 -2
- package/src/common.h +299 -0
- package/src/crc.cc +95 -0
- package/src/crc.h +23 -0
- package/src/crc_arm.cc +175 -0
- package/src/crc_common.h +4 -0
- package/{crc_folding.c → src/crc_folding.cc} +175 -185
- package/src/decoder.cc +61 -0
- package/src/decoder.h +53 -0
- package/src/decoder_avx.cc +18 -0
- package/src/decoder_avx2.cc +18 -0
- package/src/decoder_avx2_base.h +615 -0
- package/src/decoder_common.h +512 -0
- package/src/decoder_neon.cc +474 -0
- package/src/decoder_neon64.cc +451 -0
- package/src/decoder_sse2.cc +16 -0
- package/src/decoder_sse_base.h +711 -0
- package/src/decoder_ssse3.cc +18 -0
- package/src/encoder.cc +170 -0
- package/src/encoder.h +21 -0
- package/src/encoder_avx.cc +16 -0
- package/src/encoder_avx2.cc +16 -0
- package/src/encoder_avx_base.h +564 -0
- package/src/encoder_common.h +109 -0
- package/src/encoder_neon.cc +547 -0
- package/src/encoder_sse2.cc +13 -0
- package/src/encoder_sse_base.h +724 -0
- package/src/encoder_ssse3.cc +18 -0
- package/src/hedley.h +1899 -0
- package/src/platform.cc +147 -0
- package/src/yencode.cc +449 -0
- package/test/_maxsize.js +9 -0
- package/test/_speedbase.js +147 -0
- package/test/speedcrc.js +20 -0
- package/test/speeddec.js +92 -0
- package/test/speedenc.js +44 -0
- package/{testcrc.js → test/testcrc.js} +53 -39
- package/test/testdec.js +183 -0
- package/test/testenc.js +163 -0
- package/test/testpostdec.js +126 -0
- package/test.js +0 -91
- package/yencode.cc +0 -1622
package/src/platform.cc
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
#include "common.h"
|
|
2
|
+
#ifdef PLATFORM_ARM
|
|
3
|
+
# ifdef __ANDROID__
|
|
4
|
+
# include <cpu-features.h>
|
|
5
|
+
# elif defined(__linux__) || (defined(__FreeBSD__) && __FreeBSD__ >= 12)
|
|
6
|
+
# include <sys/auxv.h>
|
|
7
|
+
# include <asm/hwcap.h>
|
|
8
|
+
# elif (defined(__FreeBSD__) && __FreeBSD__ < 12)
|
|
9
|
+
# include <sys/sysctl.h>
|
|
10
|
+
# include <asm/hwcap.h>
|
|
11
|
+
# elif defined(_WIN32)
|
|
12
|
+
# define WIN32_LEAN_AND_MEAN
|
|
13
|
+
# define NOMINMAX
|
|
14
|
+
# include <Windows.h>
|
|
15
|
+
# elif defined(__APPLE__)
|
|
16
|
+
# include <sys/types.h>
|
|
17
|
+
# include <sys/sysctl.h>
|
|
18
|
+
# endif
|
|
19
|
+
bool cpu_supports_neon() {
|
|
20
|
+
# if defined(AT_HWCAP)
|
|
21
|
+
# ifdef __FreeBSD__
|
|
22
|
+
unsigned long supported;
|
|
23
|
+
elf_aux_info(AT_HWCAP, &supported, sizeof(supported));
|
|
24
|
+
# ifdef __aarch64__
|
|
25
|
+
return supported & HWCAP_ASIMD;
|
|
26
|
+
# else
|
|
27
|
+
return supported & HWCAP_NEON;
|
|
28
|
+
# endif
|
|
29
|
+
# else
|
|
30
|
+
# ifdef __aarch64__
|
|
31
|
+
return getauxval(AT_HWCAP) & HWCAP_ASIMD;
|
|
32
|
+
# else
|
|
33
|
+
return getauxval(AT_HWCAP) & HWCAP_NEON;
|
|
34
|
+
# endif
|
|
35
|
+
# endif
|
|
36
|
+
# elif defined(ANDROID_CPU_FAMILY_ARM)
|
|
37
|
+
# ifdef __aarch64__
|
|
38
|
+
return android_getCpuFeatures() & ANDROID_CPU_ARM64_FEATURE_ASIMD;
|
|
39
|
+
# else
|
|
40
|
+
return android_getCpuFeatures() & ANDROID_CPU_ARM_FEATURE_NEON;
|
|
41
|
+
# endif
|
|
42
|
+
# elif defined(_WIN32)
|
|
43
|
+
return IsProcessorFeaturePresent(PF_ARM_NEON_INSTRUCTIONS_AVAILABLE);
|
|
44
|
+
# elif defined(__APPLE__)
|
|
45
|
+
int supported = 0;
|
|
46
|
+
size_t len = sizeof(supported);
|
|
47
|
+
if(sysctlbyname("hw.optional.neon", &supported, &len, NULL, 0))
|
|
48
|
+
return false;
|
|
49
|
+
return (bool)supported;
|
|
50
|
+
# endif
|
|
51
|
+
return true; // assume NEON support, if compiled as such, otherwise (I think Windows and iOS require it)
|
|
52
|
+
}
|
|
53
|
+
#endif
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
#ifdef PLATFORM_X86
|
|
57
|
+
#ifdef _MSC_VER
|
|
58
|
+
# define _cpuid1x(ar) __cpuid(ar, 0x80000001)
|
|
59
|
+
# if _MSC_VER >= 1600
|
|
60
|
+
# define _cpuidX __cpuidex
|
|
61
|
+
# include <immintrin.h>
|
|
62
|
+
# define _GET_XCR() _xgetbv(_XCR_XFEATURE_ENABLED_MASK)
|
|
63
|
+
# else
|
|
64
|
+
// not supported
|
|
65
|
+
# define _cpuidX(ar, eax, ecx) ar[0]=0, ar[1]=0, ar[2]=0, ar[3]=0
|
|
66
|
+
# define _GET_XCR() 0
|
|
67
|
+
# endif
|
|
68
|
+
#else
|
|
69
|
+
# define _cpuid1x(ar) __cpuid(0x80000001, ar[0], ar[1], ar[2], ar[3])
|
|
70
|
+
# define _cpuidX(ar, eax, ecx) __cpuid_count(eax, ecx, ar[0], ar[1], ar[2], ar[3])
|
|
71
|
+
static inline int _GET_XCR() {
|
|
72
|
+
int xcr0;
|
|
73
|
+
__asm__ __volatile__("xgetbv" : "=a" (xcr0) : "c" (0) : "%edx");
|
|
74
|
+
return xcr0;
|
|
75
|
+
}
|
|
76
|
+
#endif
|
|
77
|
+
// checks if CPU has 128-bit AVX units; currently not used as AVX2 is beneficial even on Zen1
|
|
78
|
+
// static bool cpu_has_slow_avx(cpuid1flag0) {
|
|
79
|
+
// int family = ((cpuid1flag0>>8) & 0xf) + ((cpuid1flag0>>16) & 0xff0),
|
|
80
|
+
// model = ((cpuid1flag0>>4) & 0xf) + ((cpuid1flag0>>12) & 0xf0);
|
|
81
|
+
// return (
|
|
82
|
+
// family == 0x6f // AMD Bulldozer family
|
|
83
|
+
// || family == 0x7f // AMD Jaguar/Puma family
|
|
84
|
+
// || (family == 0x8f && (model == 0 /*Summit Ridge ES*/ || model == 1 /*Zen*/ || model == 8 /*Zen+*/ || model == 0x11 /*Zen APU*/ || model == 0x18 /*Zen+ APU*/ || model == 0x50 /*Subor Z+*/)) // AMD Zen1 family
|
|
85
|
+
// || (family == 6 && model == 0xf) // Centaur/Zhaoxin; overlaps with Intel Core 2, but they don't support AVX
|
|
86
|
+
// );
|
|
87
|
+
// }
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
int cpu_supports_isa() {
|
|
91
|
+
int flags[4];
|
|
92
|
+
_cpuid1(flags);
|
|
93
|
+
int ret = 0;
|
|
94
|
+
|
|
95
|
+
if(flags[2] & 0x800000)
|
|
96
|
+
ret |= ISA_FEATURE_POPCNT;
|
|
97
|
+
int flags2[4];
|
|
98
|
+
_cpuid1x(flags2);
|
|
99
|
+
if(flags2[2] & 0x20) // ABM
|
|
100
|
+
ret |= ISA_FEATURE_LZCNT | ISA_FEATURE_POPCNT;
|
|
101
|
+
|
|
102
|
+
int family = ((flags[0]>>8) & 0xf) + ((flags[0]>>16) & 0xff0);
|
|
103
|
+
int model = ((flags[0]>>4) & 0xf) + ((flags[0]>>12) & 0xf0);
|
|
104
|
+
|
|
105
|
+
if(family == 6 && (
|
|
106
|
+
model == 0x1C || model == 0x26 || model == 0x27 || model == 0x35 || model == 0x36 || model == 0x37 || model == 0x4A || model == 0x4C || model == 0x4D || model == 0x5A || model == 0x5D
|
|
107
|
+
))
|
|
108
|
+
// Intel Bonnell/Silvermont CPU with very slow PSHUFB and PBLENDVB - pretend SSSE3 doesn't exist
|
|
109
|
+
return ret | ISA_LEVEL_SSE2;
|
|
110
|
+
|
|
111
|
+
if(family == 0x5f && (model == 0 || model == 1 || model == 2))
|
|
112
|
+
// AMD Bobcat with slow SSSE3 instructions - pretend it doesn't exist
|
|
113
|
+
return ret | ISA_LEVEL_SSE2;
|
|
114
|
+
|
|
115
|
+
// Jaguar/Puma performance unkown (slowish PSHUFB/PBLENDVB)
|
|
116
|
+
|
|
117
|
+
if((flags[2] & 0x200) == 0x200) { // SSSE3
|
|
118
|
+
if(family == 6 && (model == 0x5c || model == 0x5f || model == 0x7a || model == 0x9c))
|
|
119
|
+
// Intel Goldmont/plus / Tremont with slow PBLENDVB
|
|
120
|
+
return ret | ISA_LEVEL_SSSE3;
|
|
121
|
+
|
|
122
|
+
if(flags[2] & 0x80000) { // SSE4.1
|
|
123
|
+
if((flags[2] & 0x18800000) == 0x18800000) { // POPCNT + OSXSAVE + AVX
|
|
124
|
+
int xcr = _GET_XCR() & 0xff; // ignore unused bits
|
|
125
|
+
if((xcr & 6) == 6) { // AVX enabled
|
|
126
|
+
int cpuInfo[4];
|
|
127
|
+
_cpuidX(cpuInfo, 7, 0);
|
|
128
|
+
if((cpuInfo[1] & 0x128) == 0x128 && (ret & ISA_FEATURE_LZCNT)) { // BMI2 + AVX2 + BMI1
|
|
129
|
+
if(((xcr & 0xE0) == 0xE0) && (cpuInfo[1] & 0xC0010000) == 0xC0010000) { // AVX512BW + AVX512VL + AVX512F
|
|
130
|
+
if(cpuInfo[2] & 0x40)
|
|
131
|
+
return ret | ISA_LEVEL_VBMI2;
|
|
132
|
+
return ret | ISA_LEVEL_AVX3;
|
|
133
|
+
}
|
|
134
|
+
// AVX2 is beneficial even on Zen1
|
|
135
|
+
return ret | ISA_LEVEL_AVX2;
|
|
136
|
+
}
|
|
137
|
+
return ret | ISA_LEVEL_AVX;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return ret | ISA_LEVEL_SSE41;
|
|
141
|
+
}
|
|
142
|
+
return ret | ISA_LEVEL_SSSE3;
|
|
143
|
+
}
|
|
144
|
+
return ret | ISA_LEVEL_SSE2;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
#endif // PLATFORM_X86
|
package/src/yencode.cc
ADDED
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
|
|
2
|
+
#include <node.h>
|
|
3
|
+
#include <node_buffer.h>
|
|
4
|
+
#include <node_version.h>
|
|
5
|
+
#include <v8.h>
|
|
6
|
+
#include <stdlib.h>
|
|
7
|
+
#include <string.h>
|
|
8
|
+
|
|
9
|
+
#include "encoder.h"
|
|
10
|
+
#include "decoder.h"
|
|
11
|
+
#include "crc.h"
|
|
12
|
+
|
|
13
|
+
using namespace v8;
|
|
14
|
+
|
|
15
|
+
static void free_buffer(char* data, void* _size) {
|
|
16
|
+
#if !NODE_VERSION_AT_LEAST(0, 11, 0)
|
|
17
|
+
int size = (int)(size_t)_size;
|
|
18
|
+
V8::AdjustAmountOfExternalAllocatedMemory(-size);
|
|
19
|
+
#endif
|
|
20
|
+
//Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory(-size);
|
|
21
|
+
free(data);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// TODO: encode should return col num for incremental processing
|
|
25
|
+
// line limit + return input consumed
|
|
26
|
+
// async processing?
|
|
27
|
+
|
|
28
|
+
static inline size_t YENC_MAX_SIZE(size_t len, size_t line_size) {
|
|
29
|
+
size_t ret = len * 2 /* all characters escaped */
|
|
30
|
+
+ 2 /* allocation for offset and that a newline may occur early */
|
|
31
|
+
#if !defined(YENC_DISABLE_AVX256)
|
|
32
|
+
+ 64 /* allocation for YMM overflowing */
|
|
33
|
+
#else
|
|
34
|
+
+ 32 /* allocation for XMM overflowing */
|
|
35
|
+
#endif
|
|
36
|
+
;
|
|
37
|
+
/* add newlines, considering the possibility of all chars escaped */
|
|
38
|
+
if(line_size == 128) // optimize common case
|
|
39
|
+
return ret + 2 * (len >> 6);
|
|
40
|
+
return ret + 2 * ((len*2) / line_size);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
#if NODE_VERSION_AT_LEAST(0, 11, 0)
|
|
46
|
+
// for node 0.12.x
|
|
47
|
+
# define FUNC(name) static void name(const FunctionCallbackInfo<Value>& args)
|
|
48
|
+
# define FUNC_START \
|
|
49
|
+
Isolate* isolate = args.GetIsolate(); \
|
|
50
|
+
HandleScope scope(isolate)
|
|
51
|
+
|
|
52
|
+
# if NODE_VERSION_AT_LEAST(8, 0, 0)
|
|
53
|
+
# define NEW_STRING(s) String::NewFromOneByte(isolate, (const uint8_t*)(s), NewStringType::kNormal).ToLocalChecked()
|
|
54
|
+
# define RETURN_ERROR(e) { isolate->ThrowException(Exception::Error(String::NewFromOneByte(isolate, (const uint8_t*)(e), NewStringType::kNormal).ToLocalChecked())); return; }
|
|
55
|
+
# define ARG_TO_INT(a) (a).As<Integer>()->Value()
|
|
56
|
+
# define ARG_TO_BOOL(a) (a).As<Boolean>()->Value()
|
|
57
|
+
# else
|
|
58
|
+
# define NEW_STRING(s) String::NewFromUtf8(isolate, s)
|
|
59
|
+
# define RETURN_ERROR(e) { isolate->ThrowException(Exception::Error(String::NewFromUtf8(isolate, e))); return; }
|
|
60
|
+
# define ARG_TO_INT(a) (a)->ToInteger()->Value()
|
|
61
|
+
# define ARG_TO_BOOL(a) (a)->ToBoolean()->Value()
|
|
62
|
+
# endif
|
|
63
|
+
# define NEW_OBJECT Object::New(isolate)
|
|
64
|
+
# if NODE_VERSION_AT_LEAST(3, 0, 0) // iojs3
|
|
65
|
+
# define NEW_BUFFER(...) node::Buffer::New(ISOLATE __VA_ARGS__).ToLocalChecked()
|
|
66
|
+
# else
|
|
67
|
+
# define NEW_BUFFER(...) node::Buffer::New(ISOLATE __VA_ARGS__)
|
|
68
|
+
# endif
|
|
69
|
+
|
|
70
|
+
# define RETURN_VAL(v) { args.GetReturnValue().Set(v); return; }
|
|
71
|
+
# define RETURN_UNDEF return
|
|
72
|
+
# define ISOLATE isolate,
|
|
73
|
+
//# define MARK_EXT_MEM isolate->AdjustAmountOfExternalAllocatedMemory
|
|
74
|
+
# define MARK_EXT_MEM(x)
|
|
75
|
+
|
|
76
|
+
#else
|
|
77
|
+
// for node 0.10.x
|
|
78
|
+
#define FUNC(name) static Handle<Value> name(const Arguments& args)
|
|
79
|
+
#define FUNC_START HandleScope scope
|
|
80
|
+
#define NEW_STRING String::New
|
|
81
|
+
#define NEW_OBJECT Object::New()
|
|
82
|
+
#define NEW_BUFFER(...) Local<Object>::New(node::Buffer::New(ISOLATE __VA_ARGS__)->handle_)
|
|
83
|
+
#define ARG_TO_INT(a) (a)->ToInteger()->Value()
|
|
84
|
+
#define ARG_TO_BOOL(a) (a)->ToBoolean()->Value()
|
|
85
|
+
|
|
86
|
+
#define RETURN_ERROR(e) \
|
|
87
|
+
return ThrowException(Exception::Error( \
|
|
88
|
+
String::New(e)) \
|
|
89
|
+
)
|
|
90
|
+
#define RETURN_VAL(v) return scope.Close(v)
|
|
91
|
+
#define RETURN_UNDEF RETURN_VAL( Undefined() )
|
|
92
|
+
#define ISOLATE
|
|
93
|
+
#define MARK_EXT_MEM V8::AdjustAmountOfExternalAllocatedMemory
|
|
94
|
+
#endif
|
|
95
|
+
|
|
96
|
+
#if NODE_VERSION_AT_LEAST(12, 0, 0)
|
|
97
|
+
# define SET_OBJ(obj, key, val) (obj)->Set(isolate->GetCurrentContext(), NEW_STRING(key), val).Check()
|
|
98
|
+
#else
|
|
99
|
+
# define SET_OBJ(obj, key, val) (obj)->Set(NEW_STRING(key), val)
|
|
100
|
+
#endif
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
// encode(str, line_size, col)
|
|
104
|
+
FUNC(Encode) {
|
|
105
|
+
FUNC_START;
|
|
106
|
+
|
|
107
|
+
if (args.Length() == 0 || !node::Buffer::HasInstance(args[0]))
|
|
108
|
+
RETURN_ERROR("You must supply a Buffer");
|
|
109
|
+
|
|
110
|
+
size_t arg_len = node::Buffer::Length(args[0]);
|
|
111
|
+
if (arg_len == 0)
|
|
112
|
+
RETURN_VAL(NEW_BUFFER(0));
|
|
113
|
+
|
|
114
|
+
int line_size = 128, col = 0;
|
|
115
|
+
if (args.Length() >= 2) {
|
|
116
|
+
line_size = (int)ARG_TO_INT(args[1]);
|
|
117
|
+
if (line_size == 0) line_size = 128; // allow this case
|
|
118
|
+
if (line_size < 0)
|
|
119
|
+
RETURN_ERROR("Line size must be at least 1 byte");
|
|
120
|
+
if (args.Length() >= 3) {
|
|
121
|
+
col = (int)ARG_TO_INT(args[2]);
|
|
122
|
+
if (col > line_size || col < 0)
|
|
123
|
+
RETURN_ERROR("Column offset cannot exceed the line size and cannot be negative");
|
|
124
|
+
if (col == line_size) col = 0; // allow this case
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// allocate enough memory to handle worst case requirements
|
|
129
|
+
size_t dest_len = YENC_MAX_SIZE(arg_len, line_size);
|
|
130
|
+
|
|
131
|
+
unsigned char *result = (unsigned char*) malloc(dest_len);
|
|
132
|
+
size_t len = do_encode(line_size, &col, (const unsigned char*)node::Buffer::Data(args[0]), result, arg_len, true);
|
|
133
|
+
result = (unsigned char*)realloc(result, len);
|
|
134
|
+
MARK_EXT_MEM(len);
|
|
135
|
+
RETURN_VAL( NEW_BUFFER((char*)result, len, free_buffer, (void*)len) );
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
FUNC(EncodeTo) {
|
|
139
|
+
FUNC_START;
|
|
140
|
+
|
|
141
|
+
if (args.Length() < 2 || !node::Buffer::HasInstance(args[0]) || !node::Buffer::HasInstance(args[1]))
|
|
142
|
+
RETURN_ERROR("You must supply two Buffers");
|
|
143
|
+
|
|
144
|
+
size_t arg_len = node::Buffer::Length(args[0]);
|
|
145
|
+
if (arg_len == 0)
|
|
146
|
+
RETURN_VAL(Integer::New(ISOLATE 0));
|
|
147
|
+
|
|
148
|
+
int line_size = 128, col = 0;
|
|
149
|
+
if (args.Length() >= 3) {
|
|
150
|
+
line_size = (int)ARG_TO_INT(args[2]);
|
|
151
|
+
if (line_size == 0) line_size = 128; // allow this case
|
|
152
|
+
if (line_size < 0)
|
|
153
|
+
RETURN_ERROR("Line size must be at least 1 byte");
|
|
154
|
+
if (args.Length() >= 4) {
|
|
155
|
+
col = (int)ARG_TO_INT(args[3]);
|
|
156
|
+
if (col > line_size || col < 0)
|
|
157
|
+
RETURN_ERROR("Column offset cannot exceed the line size and cannot be negative");
|
|
158
|
+
if (col == line_size) col = 0; // allow this case
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// check that destination buffer has enough space
|
|
163
|
+
size_t dest_len = YENC_MAX_SIZE(arg_len, line_size);
|
|
164
|
+
if(node::Buffer::Length(args[1]) < dest_len)
|
|
165
|
+
RETURN_ERROR("Destination buffer does not have enough space (use `maxSize` to compute required space)");
|
|
166
|
+
|
|
167
|
+
size_t len = do_encode(line_size, &col, (const unsigned char*)node::Buffer::Data(args[0]), (unsigned char*)node::Buffer::Data(args[1]), arg_len, true);
|
|
168
|
+
RETURN_VAL( Integer::New(ISOLATE len) );
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
FUNC(EncodeIncr) {
|
|
172
|
+
FUNC_START;
|
|
173
|
+
|
|
174
|
+
if (args.Length() == 0 || !node::Buffer::HasInstance(args[0]))
|
|
175
|
+
RETURN_ERROR("You must supply a Buffer");
|
|
176
|
+
|
|
177
|
+
int line_size = 128, col = 0;
|
|
178
|
+
bool allocResult = true;
|
|
179
|
+
unsigned char* result;
|
|
180
|
+
size_t arg_len = node::Buffer::Length(args[0]);
|
|
181
|
+
if(args.Length() > 1) {
|
|
182
|
+
int argp = 1;
|
|
183
|
+
if(node::Buffer::HasInstance(args[1])) {
|
|
184
|
+
// grab destination
|
|
185
|
+
allocResult = false;
|
|
186
|
+
// check that destination buffer has enough space
|
|
187
|
+
size_t dest_len = YENC_MAX_SIZE(arg_len, line_size);
|
|
188
|
+
if(node::Buffer::Length(args[1]) < dest_len)
|
|
189
|
+
RETURN_ERROR("Destination buffer does not have enough space (use `maxSize` to compute required space)");
|
|
190
|
+
result = (unsigned char*)node::Buffer::Data(args[1]);
|
|
191
|
+
argp++;
|
|
192
|
+
}
|
|
193
|
+
if (args.Length() > argp) {
|
|
194
|
+
line_size = (int)ARG_TO_INT(args[argp]);
|
|
195
|
+
if (line_size == 0) line_size = 128; // allow this case
|
|
196
|
+
if (line_size < 0)
|
|
197
|
+
RETURN_ERROR("Line size must be at least 1 byte");
|
|
198
|
+
argp++;
|
|
199
|
+
if (args.Length() > argp) {
|
|
200
|
+
col = (int)ARG_TO_INT(args[argp]);
|
|
201
|
+
if (col > line_size || col < 0)
|
|
202
|
+
RETURN_ERROR("Column offset cannot exceed the line size and cannot be negative");
|
|
203
|
+
if (col == line_size) col = 0; // allow this case
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
Local<Object> ret = NEW_OBJECT;
|
|
209
|
+
|
|
210
|
+
if (arg_len == 0) {
|
|
211
|
+
SET_OBJ(ret, "written", Integer::New(ISOLATE 0));
|
|
212
|
+
// TODO: set 'output'?
|
|
213
|
+
SET_OBJ(ret, "col", Integer::New(ISOLATE col));
|
|
214
|
+
RETURN_VAL( ret );
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if(allocResult) {
|
|
218
|
+
// allocate enough memory to handle worst case requirements
|
|
219
|
+
size_t dest_len = YENC_MAX_SIZE(arg_len, line_size);
|
|
220
|
+
result = (unsigned char*) malloc(dest_len);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
size_t len = do_encode(line_size, &col, (const unsigned char*)node::Buffer::Data(args[0]), result, arg_len, false);
|
|
224
|
+
|
|
225
|
+
SET_OBJ(ret, "written", Integer::New(ISOLATE len));
|
|
226
|
+
if(allocResult) {
|
|
227
|
+
result = (unsigned char*)realloc(result, len);
|
|
228
|
+
SET_OBJ(ret, "output", NEW_BUFFER((char*)result, len, free_buffer, (void*)len));
|
|
229
|
+
MARK_EXT_MEM(len);
|
|
230
|
+
}
|
|
231
|
+
SET_OBJ(ret, "col", Integer::New(ISOLATE col));
|
|
232
|
+
RETURN_VAL( ret );
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
FUNC(Decode) {
|
|
236
|
+
FUNC_START;
|
|
237
|
+
|
|
238
|
+
if (args.Length() == 0 || !node::Buffer::HasInstance(args[0]))
|
|
239
|
+
RETURN_ERROR("You must supply a Buffer");
|
|
240
|
+
|
|
241
|
+
size_t arg_len = node::Buffer::Length(args[0]);
|
|
242
|
+
if (arg_len == 0)
|
|
243
|
+
RETURN_VAL( NEW_BUFFER(0) );
|
|
244
|
+
|
|
245
|
+
bool isRaw = false;
|
|
246
|
+
if (args.Length() > 1)
|
|
247
|
+
isRaw = ARG_TO_BOOL(args[1]);
|
|
248
|
+
|
|
249
|
+
unsigned char *result = (unsigned char*) malloc(arg_len);
|
|
250
|
+
size_t len = do_decode(isRaw, (const unsigned char*)node::Buffer::Data(args[0]), result, arg_len, NULL);
|
|
251
|
+
result = (unsigned char*)realloc(result, len);
|
|
252
|
+
MARK_EXT_MEM(len);
|
|
253
|
+
RETURN_VAL( NEW_BUFFER((char*)result, len, free_buffer, (void*)len) );
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
FUNC(DecodeTo) {
|
|
257
|
+
FUNC_START;
|
|
258
|
+
|
|
259
|
+
if (args.Length() < 2 || !node::Buffer::HasInstance(args[0]) || !node::Buffer::HasInstance(args[1]))
|
|
260
|
+
RETURN_ERROR("You must supply two Buffers");
|
|
261
|
+
|
|
262
|
+
size_t arg_len = node::Buffer::Length(args[0]);
|
|
263
|
+
if (arg_len == 0)
|
|
264
|
+
RETURN_VAL( Integer::New(ISOLATE 0) );
|
|
265
|
+
|
|
266
|
+
// check that destination buffer has enough space
|
|
267
|
+
if(node::Buffer::Length(args[1]) < arg_len)
|
|
268
|
+
RETURN_VAL( Integer::New(ISOLATE 0) );
|
|
269
|
+
|
|
270
|
+
bool isRaw = false;
|
|
271
|
+
if (args.Length() > 2)
|
|
272
|
+
isRaw = ARG_TO_BOOL(args[2]);
|
|
273
|
+
|
|
274
|
+
size_t len = do_decode(isRaw, (const unsigned char*)node::Buffer::Data(args[0]), (unsigned char*)node::Buffer::Data(args[1]), arg_len, NULL);
|
|
275
|
+
RETURN_VAL( Integer::New(ISOLATE len) );
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
FUNC(DecodeIncr) {
|
|
280
|
+
FUNC_START;
|
|
281
|
+
|
|
282
|
+
if (args.Length() == 0 || !node::Buffer::HasInstance(args[0]))
|
|
283
|
+
RETURN_ERROR("You must supply a Buffer");
|
|
284
|
+
|
|
285
|
+
size_t arg_len = node::Buffer::Length(args[0]);
|
|
286
|
+
if (arg_len == 0)
|
|
287
|
+
// handled properly in Javascript
|
|
288
|
+
RETURN_UNDEF;
|
|
289
|
+
|
|
290
|
+
YencDecoderState state = YDEC_STATE_CRLF;
|
|
291
|
+
unsigned char *result = NULL;
|
|
292
|
+
bool allocResult = true;
|
|
293
|
+
if (args.Length() > 1) {
|
|
294
|
+
state = (YencDecoderState)(ARG_TO_INT(args[1]));
|
|
295
|
+
if (args.Length() > 2 && node::Buffer::HasInstance(args[2])) {
|
|
296
|
+
if(node::Buffer::Length(args[2]) < arg_len)
|
|
297
|
+
RETURN_ERROR("Destination buffer does not have enough space");
|
|
298
|
+
result = (unsigned char*)node::Buffer::Data(args[2]);
|
|
299
|
+
allocResult = false;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
const unsigned char* src = (const unsigned char*)node::Buffer::Data(args[0]);
|
|
304
|
+
const unsigned char* sp = src;
|
|
305
|
+
#ifdef DBG_ALIGN_SOURCE
|
|
306
|
+
void* newSrc = valloc(arg_len);
|
|
307
|
+
memcpy(newSrc, src, arg_len);
|
|
308
|
+
sp = (const unsigned char*)newSrc;
|
|
309
|
+
#endif
|
|
310
|
+
|
|
311
|
+
if(allocResult) result = (unsigned char*) malloc(arg_len);
|
|
312
|
+
unsigned char* dp = result;
|
|
313
|
+
YencDecoderEnd ended = do_decode_end(&sp, &dp, arg_len, &state);
|
|
314
|
+
size_t len = dp - result;
|
|
315
|
+
if(allocResult) result = (unsigned char*)realloc(result, len);
|
|
316
|
+
|
|
317
|
+
#ifdef DBG_ALIGN_SOURCE
|
|
318
|
+
free(newSrc);
|
|
319
|
+
#endif
|
|
320
|
+
|
|
321
|
+
Local<Object> ret = NEW_OBJECT;
|
|
322
|
+
SET_OBJ(ret, "read", Integer::New(ISOLATE sp - src));
|
|
323
|
+
SET_OBJ(ret, "written", Integer::New(ISOLATE len));
|
|
324
|
+
if(allocResult) {
|
|
325
|
+
SET_OBJ(ret, "output", NEW_BUFFER((char*)result, len, free_buffer, (void*)len));
|
|
326
|
+
MARK_EXT_MEM(len);
|
|
327
|
+
}
|
|
328
|
+
SET_OBJ(ret, "ended", Integer::New(ISOLATE (int)ended));
|
|
329
|
+
SET_OBJ(ret, "state", Integer::New(ISOLATE state));
|
|
330
|
+
RETURN_VAL( ret );
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
static inline uint32_t read_crc32(const Local<Value>& buf) {
|
|
335
|
+
const uint8_t* arr = (const uint8_t*)node::Buffer::Data(buf);
|
|
336
|
+
return (((uint_fast32_t)arr[0] << 24) | ((uint_fast32_t)arr[1] << 16) | ((uint_fast32_t)arr[2] << 8) | (uint_fast32_t)arr[3]);
|
|
337
|
+
}
|
|
338
|
+
static inline Local<Object> pack_crc32(
|
|
339
|
+
#if NODE_VERSION_AT_LEAST(0, 11, 0)
|
|
340
|
+
Isolate* isolate,
|
|
341
|
+
#endif
|
|
342
|
+
uint32_t crc) {
|
|
343
|
+
Local<Object> buff = NEW_BUFFER(4);
|
|
344
|
+
unsigned char* d = (unsigned char*)node::Buffer::Data(buff);
|
|
345
|
+
d[0] = (unsigned char)(crc >> 24) & 0xFF;
|
|
346
|
+
d[1] = (unsigned char)(crc >> 16) & 0xFF;
|
|
347
|
+
d[2] = (unsigned char)(crc >> 8) & 0xFF;
|
|
348
|
+
d[3] = (unsigned char)crc & 0xFF;
|
|
349
|
+
return buff;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// crc32(str, init)
|
|
353
|
+
FUNC(CRC32) {
|
|
354
|
+
FUNC_START;
|
|
355
|
+
|
|
356
|
+
if (args.Length() == 0 || !node::Buffer::HasInstance(args[0]))
|
|
357
|
+
RETURN_ERROR("You must supply a Buffer");
|
|
358
|
+
// TODO: support string args??
|
|
359
|
+
|
|
360
|
+
uint32_t crc = 0;
|
|
361
|
+
if (args.Length() >= 2) {
|
|
362
|
+
if (!node::Buffer::HasInstance(args[1]) || node::Buffer::Length(args[1]) != 4)
|
|
363
|
+
RETURN_ERROR("Second argument must be a 4 byte buffer");
|
|
364
|
+
crc = read_crc32(args[1]);
|
|
365
|
+
}
|
|
366
|
+
crc = do_crc32(
|
|
367
|
+
(const void*)node::Buffer::Data(args[0]),
|
|
368
|
+
node::Buffer::Length(args[0]),
|
|
369
|
+
crc
|
|
370
|
+
);
|
|
371
|
+
RETURN_VAL(pack_crc32(ISOLATE crc));
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
FUNC(CRC32Combine) {
|
|
375
|
+
FUNC_START;
|
|
376
|
+
|
|
377
|
+
if (args.Length() < 3)
|
|
378
|
+
RETURN_ERROR("At least 3 arguments required");
|
|
379
|
+
if (!node::Buffer::HasInstance(args[0]) || node::Buffer::Length(args[0]) != 4
|
|
380
|
+
|| !node::Buffer::HasInstance(args[1]) || node::Buffer::Length(args[1]) != 4)
|
|
381
|
+
RETURN_ERROR("You must supply a 4 byte Buffer for the first two arguments");
|
|
382
|
+
|
|
383
|
+
uint32_t crc1 = read_crc32(args[0]), crc2 = read_crc32(args[1]);
|
|
384
|
+
size_t len = (size_t)ARG_TO_INT(args[2]);
|
|
385
|
+
|
|
386
|
+
crc1 = do_crc32_combine(crc1, crc2, len);
|
|
387
|
+
RETURN_VAL(pack_crc32(ISOLATE crc1));
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
FUNC(CRC32Zeroes) {
|
|
391
|
+
FUNC_START;
|
|
392
|
+
|
|
393
|
+
if (args.Length() < 1)
|
|
394
|
+
RETURN_ERROR("At least 1 argument required");
|
|
395
|
+
|
|
396
|
+
uint32_t crc1 = 0;
|
|
397
|
+
if (args.Length() >= 2) {
|
|
398
|
+
if (!node::Buffer::HasInstance(args[1]) || node::Buffer::Length(args[1]) != 4)
|
|
399
|
+
RETURN_ERROR("Second argument must be a 4 byte buffer");
|
|
400
|
+
crc1 = read_crc32(args[1]);
|
|
401
|
+
}
|
|
402
|
+
size_t len = (size_t)ARG_TO_INT(args[0]);
|
|
403
|
+
crc1 = do_crc32_zeros(crc1, len);
|
|
404
|
+
RETURN_VAL(pack_crc32(ISOLATE crc1));
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
static void init_all() {
|
|
408
|
+
encoder_init();
|
|
409
|
+
decoder_init();
|
|
410
|
+
crc_init();
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
#if NODE_VERSION_AT_LEAST(10, 7, 0)
|
|
414
|
+
// signal context aware module for node if it supports it
|
|
415
|
+
# include <uv.h>
|
|
416
|
+
static uv_once_t init_once = UV_ONCE_INIT;
|
|
417
|
+
NODE_MODULE_INIT(/* exports, module, context */)
|
|
418
|
+
#else
|
|
419
|
+
void yencode_init(
|
|
420
|
+
# if NODE_VERSION_AT_LEAST(4, 0, 0)
|
|
421
|
+
Local<Object> exports,
|
|
422
|
+
Local<Value> module,
|
|
423
|
+
void* priv
|
|
424
|
+
# else
|
|
425
|
+
Handle<Object> exports
|
|
426
|
+
# endif
|
|
427
|
+
)
|
|
428
|
+
#endif
|
|
429
|
+
{
|
|
430
|
+
NODE_SET_METHOD(exports, "encode", Encode);
|
|
431
|
+
NODE_SET_METHOD(exports, "encodeTo", EncodeTo);
|
|
432
|
+
NODE_SET_METHOD(exports, "encodeIncr", EncodeIncr);
|
|
433
|
+
NODE_SET_METHOD(exports, "decode", Decode);
|
|
434
|
+
NODE_SET_METHOD(exports, "decodeTo", DecodeTo);
|
|
435
|
+
NODE_SET_METHOD(exports, "decodeIncr", DecodeIncr);
|
|
436
|
+
NODE_SET_METHOD(exports, "crc32", CRC32);
|
|
437
|
+
NODE_SET_METHOD(exports, "crc32_combine", CRC32Combine);
|
|
438
|
+
NODE_SET_METHOD(exports, "crc32_zeroes", CRC32Zeroes);
|
|
439
|
+
|
|
440
|
+
#if NODE_VERSION_AT_LEAST(10, 7, 0)
|
|
441
|
+
uv_once(&init_once, init_all);
|
|
442
|
+
#else
|
|
443
|
+
init_all();
|
|
444
|
+
#endif
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
#if !NODE_VERSION_AT_LEAST(10, 7, 0)
|
|
448
|
+
NODE_MODULE(yencode, yencode_init);
|
|
449
|
+
#endif
|
package/test/_maxsize.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
module.exports = function(length, line_size) {
|
|
3
|
+
if(!length) return 0;
|
|
4
|
+
return Math.ceil(length*2) // all characters escaped
|
|
5
|
+
+ 2 * Math.floor((length*2) / (line_size||128)) // newlines, considering the possibility of all chars escaped
|
|
6
|
+
+ 2 // allocation for offset and that a newline may occur early
|
|
7
|
+
+ 64 // extra space just in case things go awry... just kidding, it's just extra padding to make SIMD logic easier
|
|
8
|
+
;
|
|
9
|
+
};
|