1313 * You should have received a copy of the GNU Lesser General Public License
1414 * along with this program. If not, see <http://www.gnu.org/licenses/>.
1515 */
16- #define __STDC_LIMIT_MACROS
17-
1816#include < napi.h>
1917
2018#include < algorithm>
21-
22- #include < stdint.h >
23- #include < string.h >
19+ # include < bit >
20+ #include < cstdint >
21+ #include < cstring >
2422
2523#define CHECK_CONDITION (expr, str ) \
2624 if (!(expr)) { \
2725 Napi::TypeError::New (env, str).ThrowAsJavaScriptException (); \
2826 return env.Undefined (); \
2927 }
3028
31- typedef enum ENDIANESS
29+ enum class ENDIANESS
3230{
33- ENDIANESS_MOTOROLA = 0 ,
34- ENDIANESS_INTEL
35- } ENDIANESS ;
31+ MOTOROLA = 0 ,
32+ INTEL
33+ };
3634
37- typedef enum SIGNAL_TYPE
35+ enum class SIGNAL_TYPE
3836{
39- SIGNAL_TYPE_UNSIGNED = 0 ,
40- SIGNAL_TYPE_SIGNED = 1 ,
41- SIGNAL_TYPE_FLOAT32 = 2 ,
42- SIGNAL_TYPE_FLOAT64 = 3
43- } SIGNAL_TYPE ;
37+ UNSIGNED = 0 ,
38+ SIGNED = 1 ,
39+ FLOAT32 = 2 ,
40+ FLOAT64 = 3
41+ };
4442
45- static SIGNAL_TYPE _parse_signal_type (const Napi::Value& v)
43+ static SIGNAL_TYPE _parse_signal_type (Napi::Env env, const Napi::Value& v)
4644{
4745 if (v.IsBoolean ())
48- return v.As <Napi::Boolean>().Value () ? SIGNAL_TYPE_SIGNED : SIGNAL_TYPE_UNSIGNED ;
46+ return v.As <Napi::Boolean>().Value () ? SIGNAL_TYPE::SIGNED : SIGNAL_TYPE::UNSIGNED ;
4947 uint32_t n = v.As <Napi::Number>().Uint32Value ();
50- if (n > SIGNAL_TYPE_FLOAT64)
51- return SIGNAL_TYPE_UNSIGNED; // safe fallback for unknown values
52- return (SIGNAL_TYPE)n;
48+ if (n > static_cast <uint32_t >(SIGNAL_TYPE::FLOAT64)) {
49+ Napi::TypeError::New (env, " Unknown signal type" ).ThrowAsJavaScriptException ();
50+ return SIGNAL_TYPE::UNSIGNED;
51+ }
52+ return static_cast <SIGNAL_TYPE>(n);
5353}
5454
5555// Returns the fixed IEEE-754 bit width for float signal types; 0 for integer types.
5656static uint32_t signal_type_bit_width (SIGNAL_TYPE t)
5757{
5858 switch (t) {
59- case SIGNAL_TYPE_FLOAT32 : return 32 ;
60- case SIGNAL_TYPE_FLOAT64 : return 64 ;
61- default : return 0 ;
59+ case SIGNAL_TYPE::FLOAT32 : return 32 ;
60+ case SIGNAL_TYPE::FLOAT64 : return 64 ;
61+ default : return 0 ;
6262 }
6363}
6464
6565// -----------------------------------------------------------------------------------------
6666// _signals.* methods
6767
68- static u_int64_t _getvalue (u_int8_t * data,
69- u_int32_t offset,
70- u_int32_t length,
71- ENDIANESS byteOrder)
68+ [[nodiscard]] static uint64_t _getvalue (const uint8_t * data,
69+ uint32_t offset,
70+ uint32_t length,
71+ ENDIANESS byteOrder)
7272{
73- uint64_t d;
74- uint64_t o = 0 ;
75-
76- if (byteOrder == ENDIANESS_INTEL) {
77- d = le64toh (*((uint64_t *)&data[0 ]));
78- } else {
79- d = be64toh (*((uint64_t *)&data[0 ]));
80- }
73+ uint64_t d_raw;
74+ std::memcpy (&d_raw, data, sizeof (d_raw));
75+ uint64_t d = (byteOrder == ENDIANESS::INTEL) ? le64toh (d_raw) : be64toh (d_raw);
8176
82- uint64_t m;
83- if (length == 64 ) {
84- m = (uint64_t ) UINT64_MAX;
85- } else {
86- m = (1LLU << length) - 1 ;
87- }
88- size_t shift;
89- if (byteOrder == ENDIANESS_INTEL) {
90- shift = offset;
91- } else {
92- shift = 64 - offset - length;
93- }
77+ uint64_t m = (length == 64 ) ? UINT64_MAX : (UINT64_C (1 ) << length) - 1 ;
78+ size_t shift = (byteOrder == ENDIANESS::INTEL) ? offset : (64 - offset - length);
9479
95- o = (d >> shift) & m;
80+ uint64_t o = (d >> shift) & m;
9681
9782#ifdef KAYAK_DATA_CHECK
9883 size_t i;
9984 int bitNr;
10085 uint64_t val = 0 ;
101- if (byteOrder == ENDIANESS_INTEL ) {
86+ if (byteOrder == ENDIANESS::INTEL ) {
10287 for (i = 0 ; i < length; i++) {
10388 bitNr = i + offset;
10489 val |= ((data[bitNr >> 3 ] >> (bitNr & 0x07 )) & 1 ) << i;
@@ -142,8 +127,9 @@ Napi::Value DecodeSignal(const Napi::CallbackInfo& info)
142127
143128 offset = info[1 ].As <Napi::Number>().Uint32Value ();
144129 bitLength = info[2 ].As <Napi::Number>().Uint32Value ();
145- endianess = info[3 ].As <Napi::Boolean>().Value () ? ENDIANESS_INTEL : ENDIANESS_MOTOROLA;
146- SIGNAL_TYPE signalType = _parse_signal_type (info[4 ]);
130+ endianess = info[3 ].As <Napi::Boolean>().Value () ? ENDIANESS::INTEL : ENDIANESS::MOTOROLA;
131+ SIGNAL_TYPE signalType = _parse_signal_type (env, info[4 ]);
132+ if (env.IsExceptionPending ()) return env.Undefined ();
147133
148134 // Float types have a fixed width dictated by the type, not the caller.
149135 // Using signal_type_bit_width() ensures encode and decode always agree.
@@ -152,78 +138,60 @@ Napi::Value DecodeSignal(const Napi::CallbackInfo& info)
152138
153139 size_t maxBytes = std::min<size_t >(jsData.ByteLength (), sizeof (data));
154140
155- memset (data, 0 , sizeof (data));
156- memcpy (data, jsData.Data (), maxBytes);
141+ std:: memset (data, 0 , sizeof (data));
142+ std:: memcpy (data, jsData.Data (), maxBytes);
157143
158144 uint64_t val = _getvalue (data, offset, effectiveBitLength, endianess);
159145
160146 Napi::Value retval;
161- if (signalType == SIGNAL_TYPE_FLOAT32 ) {
147+ if (signalType == SIGNAL_TYPE::FLOAT32 ) {
162148 // Reinterpret the 32 raw bits as IEEE-754 single precision.
163- // _getvalue already applied the correct byte order, so a plain
164- // memcpy into a float gives the right value on any host endianness.
165- uint32_t raw = (uint32_t )val;
166- float f;
167- memcpy (&f, &raw, sizeof (f));
168- retval = Napi::Number::New (env, (double )f);
169- } else if (signalType == SIGNAL_TYPE_FLOAT64) {
170- double d;
171- memcpy (&d, &val, sizeof (d));
172- retval = Napi::Number::New (env, d);
173- } else if (signalType == SIGNAL_TYPE_SIGNED && val & (1LLU << (bitLength - 1 ))) {
174- int32_t tmp = -1 * (~((UINT64_MAX << bitLength) | val) + 1 );
175- retval = Napi::Number::New (env, tmp);
149+ // _getvalue already applied the correct byte order, so std::bit_cast
150+ // gives the right value on any host endianness.
151+ retval = Napi::Number::New (env,
152+ static_cast <double >(std::bit_cast<float >(static_cast <uint32_t >(val))));
153+ } else if (signalType == SIGNAL_TYPE::FLOAT64) {
154+ retval = Napi::Number::New (env, std::bit_cast<double >(val));
155+ } else if (signalType == SIGNAL_TYPE::SIGNED && (val & (UINT64_C (1 ) << (bitLength - 1 )))) {
156+ // Sign-extend from bitLength bits to int64_t.
157+ // XOR-subtract avoids UB that a naive left-shift approach would have.
158+ uint64_t sign_mask = UINT64_C (1 ) << (bitLength - 1 );
159+ int64_t tmp = static_cast <int64_t >((val ^ sign_mask) - sign_mask);
160+ retval = Napi::Number::New (env, static_cast <double >(tmp));
176161 } else {
177- retval = Napi::Number::New (env, ( uint32_t ) val);
162+ retval = Napi::Number::New (env, static_cast < uint32_t >( val) );
178163 }
179164
180165 Napi::Array raw_values = Napi::Array::New (env, 2 );
181166 raw_values.Set (0u , retval);
182167 // For float types the full value is in index 0; index 1 is always 0.
183- uint32_t hi = (width > 0 ) ? 0u : ( uint32_t ) (val >> 32 );
168+ uint32_t hi = (width > 0 ) ? 0u : static_cast < uint32_t > (val >> 32 );
184169 raw_values.Set (1u , Napi::Number::New (env, hi));
185170
186171 return raw_values;
187172}
188173
189- void _setvalue (u_int32_t offset, u_int32_t bitLength, ENDIANESS endianess, u_int8_t data[8 ], u_int64_t raw_value)
174+ static void _setvalue (uint32_t offset, uint32_t bitLength, ENDIANESS endianess,
175+ uint8_t * data, uint64_t raw_value)
190176{
191- uint64_t o;
192- if (endianess == ENDIANESS_INTEL) {
193- o = le64toh (*((uint64_t *)&data[0 ]));
194- } else {
195- o = be64toh (*((uint64_t *)&data[0 ]));
196- }
197-
198- uint64_t m = 0 ;
199- if (bitLength == 64 ) {
200- m = (uint64_t ) UINT64_MAX;
201- } else {
202- m = (1LLU << bitLength) - 1 ;
203- }
204- size_t shift;
205- if (endianess == ENDIANESS_INTEL) {
206- shift = offset;
207- } else {
208- shift = 64 - offset - bitLength;
209- }
177+ uint64_t o_raw;
178+ std::memcpy (&o_raw, data, sizeof (o_raw));
179+ uint64_t o = (endianess == ENDIANESS::INTEL) ? le64toh (o_raw) : be64toh (o_raw);
210180
211- o & = (uint64_t ) ~(m << shift) ;
212- o | = (uint64_t ) (raw_value & m) << shift ;
181+ uint64_t m = (bitLength == 64 ) ? UINT64_MAX : ( UINT64_C ( 1 ) << bitLength) - 1 ;
182+ size_t shift = (endianess == ENDIANESS::INTEL) ? offset : ( 64 - offset - bitLength) ;
213183
214- if (endianess == ENDIANESS_INTEL) {
215- o = htole64 (o);
216- } else {
217- o = htobe64 (o);
218- }
184+ o &= ~(m << shift);
185+ o |= (raw_value & m) << shift;
219186
220- memcpy (&data[0 ], &o, 8 );
187+ o = (endianess == ENDIANESS::INTEL) ? htole64 (o) : htobe64 (o);
188+ std::memcpy (data, &o, sizeof (o));
221189
222190#ifdef KAYAK_DATA_CHECK
223191 size_t i;
224192 int bitNr;
225193 uint64_t val = 0 ;
226- if (endianess == ENDIANESS_INTEL ) {
194+ if (endianess == ENDIANESS::INTEL ) {
227195 for (i = 0 ; i < bitLength; i++) {
228196 bitNr = i + offset;
229197 val |= ((data[bitNr >> 3 ] >> (bitNr & 0x07 )) & 1 ) << i;
@@ -267,31 +235,30 @@ Napi::Value EncodeSignal(const Napi::CallbackInfo& info)
267235
268236 offset = info[1 ].As <Napi::Number>().Uint32Value ();
269237 bitLength = info[2 ].As <Napi::Number>().Uint32Value ();
270- endianess = info[3 ].As <Napi::Boolean>().Value () ? ENDIANESS_INTEL : ENDIANESS_MOTOROLA;
271- SIGNAL_TYPE signalType = _parse_signal_type (info[4 ]);
238+ endianess = info[3 ].As <Napi::Boolean>().Value () ? ENDIANESS::INTEL : ENDIANESS::MOTOROLA;
239+ SIGNAL_TYPE signalType = _parse_signal_type (env, info[4 ]);
240+ if (env.IsExceptionPending ()) return env.Undefined ();
272241
273242 size_t maxBytes = std::min<size_t >(jsData.ByteLength (), sizeof (data));
274- memcpy (data, jsData.Data (), maxBytes);
275-
276- if (signalType == SIGNAL_TYPE_FLOAT32) {
277- float f = (float )info[5 ].As <Napi::Number>().DoubleValue ();
278- uint32_t raw;
279- memcpy (&raw, &f, sizeof (raw));
280- _setvalue (offset, signal_type_bit_width (SIGNAL_TYPE_FLOAT32), endianess, data, (uint64_t )raw);
281- } else if (signalType == SIGNAL_TYPE_FLOAT64) {
243+ std::memcpy (data, jsData.Data (), maxBytes);
244+
245+ if (signalType == SIGNAL_TYPE::FLOAT32) {
246+ float f = static_cast <float >(info[5 ].As <Napi::Number>().DoubleValue ());
247+ _setvalue (offset, signal_type_bit_width (SIGNAL_TYPE::FLOAT32), endianess, data,
248+ static_cast <uint64_t >(std::bit_cast<uint32_t >(f)));
249+ } else if (signalType == SIGNAL_TYPE::FLOAT64) {
282250 double d = info[5 ].As <Napi::Number>().DoubleValue ();
283- uint64_t raw;
284- memcpy (&raw, &d, sizeof (raw));
285- _setvalue (offset, signal_type_bit_width (SIGNAL_TYPE_FLOAT64), endianess, data, raw);
251+ _setvalue (offset, signal_type_bit_width (SIGNAL_TYPE::FLOAT64), endianess, data,
252+ std::bit_cast<uint64_t >(d));
286253 } else {
287- uint64_t raw_value = info[5 ].As <Napi::Number>().Uint32Value ();
254+ uint64_t raw_value = static_cast < uint64_t >( info[5 ].As <Napi::Number>().Uint32Value () );
288255 if (info.Length () > 6 && (info[6 ].IsNumber () || info[6 ].IsBoolean ())) {
289- raw_value += (( uint64_t ) info[6 ].As <Napi::Number>().Uint32Value ()) << 32 ;
256+ raw_value += static_cast < uint64_t >( info[6 ].As <Napi::Number>().Uint32Value ()) << 32 ;
290257 }
291258 _setvalue (offset, bitLength, endianess, data, raw_value);
292259 }
293260
294- memcpy (jsData.Data (), data, maxBytes);
261+ std:: memcpy (jsData.Data (), data, maxBytes);
295262
296263 return env.Undefined ();
297264}
0 commit comments