mini_racer 0.21.4 → 0.22.1
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.
- checksums.yaml +4 -4
- data/CHANGELOG +11 -0
- data/README.md +57 -0
- data/ext/mini_racer_extension/mini_racer_extension.c +97 -39
- data/ext/mini_racer_extension/mini_racer_v8.cc +180 -17
- data/ext/mini_racer_extension/mini_racer_v8.h +5 -1
- data/ext/mini_racer_extension/serde.c +17 -10
- data/lib/mini_racer/shared.rb +151 -111
- data/lib/mini_racer/truffleruby.rb +89 -66
- data/lib/mini_racer/version.rb +1 -1
- data/lib/mini_racer.rb +17 -9
- metadata +33 -5
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
#include "v8-profiler.h"
|
|
3
3
|
#include "libplatform/libplatform.h"
|
|
4
4
|
#include "mini_racer_v8.h"
|
|
5
|
+
#include <atomic>
|
|
5
6
|
#include <memory>
|
|
6
7
|
#include <vector>
|
|
7
8
|
#include <cassert>
|
|
@@ -68,6 +69,12 @@ struct Callback
|
|
|
68
69
|
int32_t id;
|
|
69
70
|
};
|
|
70
71
|
|
|
72
|
+
enum : unsigned
|
|
73
|
+
{
|
|
74
|
+
NON_WATCHDOG_TERMINATION = 1,
|
|
75
|
+
WATCHDOG_TERMINATION = 2,
|
|
76
|
+
};
|
|
77
|
+
|
|
71
78
|
// NOTE: do *not* use thread_locals to store state. In single-threaded
|
|
72
79
|
// mode, V8 runs on the same thread as Ruby and the Ruby runtime clobbers
|
|
73
80
|
// thread-locals when it context-switches threads. Ruby 3.4.0 has a new
|
|
@@ -91,6 +98,14 @@ struct State
|
|
|
91
98
|
Context *ruby_context;
|
|
92
99
|
int64_t max_memory;
|
|
93
100
|
int err_reason;
|
|
101
|
+
// TerminateExecution() while idle doesn't make IsExecutionTerminating() true
|
|
102
|
+
std::atomic<unsigned> terminate_requested;
|
|
103
|
+
// Armed before checking terminate_requested so a concurrent terminator
|
|
104
|
+
// either gets observed or posts a task. Uses sequential consistency.
|
|
105
|
+
std::atomic<bool> waiting_for_task;
|
|
106
|
+
// Tracks reentrant call/eval dispatches so nested await calls can fail
|
|
107
|
+
// instead of deadlocking and nested pumps preserve outer termination.
|
|
108
|
+
int javascript_call_depth;
|
|
94
109
|
bool verbose_exceptions;
|
|
95
110
|
std::vector<Callback*> callbacks;
|
|
96
111
|
std::unique_ptr<v8::ArrayBuffer::Allocator> allocator;
|
|
@@ -416,6 +431,7 @@ void v8_gc_callback(v8::Isolate*, v8::GCType, v8::GCCallbackFlags, void *data)
|
|
|
416
431
|
int64_t used_heap_size = static_cast<int64_t>(s.used_heap_size());
|
|
417
432
|
if (used_heap_size > st.max_memory) {
|
|
418
433
|
st.err_reason = MEMORY_ERROR;
|
|
434
|
+
st.terminate_requested.fetch_or(NON_WATCHDOG_TERMINATION);
|
|
419
435
|
st.isolate->TerminateExecution();
|
|
420
436
|
}
|
|
421
437
|
}
|
|
@@ -586,8 +602,76 @@ fail:
|
|
|
586
602
|
reply_retry(st, err);
|
|
587
603
|
}
|
|
588
604
|
|
|
605
|
+
struct JavascriptCallScope
|
|
606
|
+
{
|
|
607
|
+
int& depth;
|
|
608
|
+
|
|
609
|
+
explicit JavascriptCallScope(int& depth) : depth(depth) { depth++; }
|
|
610
|
+
~JavascriptCallScope() { depth--; }
|
|
611
|
+
};
|
|
612
|
+
|
|
613
|
+
void throw_nested_await_call(State& st)
|
|
614
|
+
{
|
|
615
|
+
// V8 does not run microtask checkpoints recursively. A nested await call
|
|
616
|
+
// from an attached Ruby callback can therefore deadlock, so reject it
|
|
617
|
+
// before entering JavaScript.
|
|
618
|
+
auto message = v8::String::NewFromUtf8Literal(
|
|
619
|
+
st.isolate, "nested call_await/eval_await is not supported");
|
|
620
|
+
st.isolate->ThrowException(v8::Exception::Error(message));
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
// awaits |*result| if it's a promise; false means an exception is pending
|
|
624
|
+
bool await_promise(State& st, v8::Local<v8::Value> *result)
|
|
625
|
+
{
|
|
626
|
+
if (!(*result)->IsPromise()) return true;
|
|
627
|
+
auto promise = result->As<v8::Promise>();
|
|
628
|
+
for (;;) {
|
|
629
|
+
v8::MicrotasksScope::PerformCheckpoint(st.isolate);
|
|
630
|
+
switch (promise->State()) {
|
|
631
|
+
case v8::Promise::kFulfilled:
|
|
632
|
+
*result = promise->Result();
|
|
633
|
+
return true;
|
|
634
|
+
case v8::Promise::kRejected:
|
|
635
|
+
st.isolate->ThrowException(promise->Result());
|
|
636
|
+
return false;
|
|
637
|
+
case v8::Promise::kPending:
|
|
638
|
+
break;
|
|
639
|
+
}
|
|
640
|
+
st.waiting_for_task.store(true);
|
|
641
|
+
if (st.terminate_requested.load() || st.isolate->IsExecutionTerminating()) {
|
|
642
|
+
st.waiting_for_task.store(false);
|
|
643
|
+
return false;
|
|
644
|
+
}
|
|
645
|
+
// blocks until the next task; a concurrent v8_terminate_execution
|
|
646
|
+
// observes waiting_for_task and posts one to end the wait
|
|
647
|
+
v8::platform::PumpMessageLoop(
|
|
648
|
+
platform, st.isolate,
|
|
649
|
+
v8::platform::MessageLoopBehavior::kWaitForWork);
|
|
650
|
+
st.waiting_for_task.store(false);
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
// Nested calls report termination but leave it pending for the enclosing call.
|
|
655
|
+
// Outermost calls consume it as before.
|
|
656
|
+
bool suspend_termination(State& st, bool nested, int& cause)
|
|
657
|
+
{
|
|
658
|
+
unsigned requests = nested ? st.terminate_requested.load()
|
|
659
|
+
: st.terminate_requested.exchange(0);
|
|
660
|
+
if (!requests && !st.isolate->IsExecutionTerminating()) return false;
|
|
661
|
+
st.isolate->CancelTerminateExecution();
|
|
662
|
+
cause = st.err_reason ? st.err_reason : TERMINATED_ERROR;
|
|
663
|
+
if (!nested) st.err_reason = NO_ERROR;
|
|
664
|
+
return nested;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
void restore_termination(State& st, bool suspended)
|
|
668
|
+
{
|
|
669
|
+
if (suspended)
|
|
670
|
+
st.isolate->TerminateExecution();
|
|
671
|
+
}
|
|
672
|
+
|
|
589
673
|
// response is errback [result, err] array
|
|
590
|
-
|
|
674
|
+
void v8_call_impl(State *pst, const uint8_t *p, size_t n, bool await)
|
|
591
675
|
{
|
|
592
676
|
State& st = *pst;
|
|
593
677
|
v8::TryCatch try_catch(st.isolate);
|
|
@@ -598,6 +682,14 @@ extern "C" void v8_call(State *pst, const uint8_t *p, size_t n)
|
|
|
598
682
|
des.ReadHeader(st.context).Check();
|
|
599
683
|
v8::Local<v8::Value> result;
|
|
600
684
|
int cause = INTERNAL_ERROR;
|
|
685
|
+
bool preserve_termination = false;
|
|
686
|
+
bool nested = st.javascript_call_depth > 0;
|
|
687
|
+
JavascriptCallScope call_scope(st.javascript_call_depth);
|
|
688
|
+
if (await && nested) {
|
|
689
|
+
throw_nested_await_call(st);
|
|
690
|
+
cause = RUNTIME_ERROR;
|
|
691
|
+
goto fail;
|
|
692
|
+
}
|
|
601
693
|
{
|
|
602
694
|
v8::Local<v8::Value> request_v;
|
|
603
695
|
if (!des.ReadValue(st.context).ToLocal(&request_v)) goto fail;
|
|
@@ -645,16 +737,16 @@ extern "C" void v8_call(State *pst, const uint8_t *p, size_t n)
|
|
|
645
737
|
auto maybe_result_v = function->Call(st.context, obj, args.size(), args.data());
|
|
646
738
|
v8::Local<v8::Value> result_v;
|
|
647
739
|
if (!maybe_result_v.ToLocal(&result_v)) goto fail;
|
|
740
|
+
if (await && !await_promise(st, &result_v)) goto fail;
|
|
648
741
|
result = sanitize(st, result_v);
|
|
649
742
|
}
|
|
650
743
|
cause = NO_ERROR;
|
|
651
744
|
fail:
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
745
|
+
preserve_termination = suspend_termination(st, nested, cause);
|
|
746
|
+
if (bubble_up_ruby_exception(st, &try_catch)) {
|
|
747
|
+
restore_termination(st, preserve_termination);
|
|
748
|
+
return;
|
|
656
749
|
}
|
|
657
|
-
if (bubble_up_ruby_exception(st, &try_catch)) return;
|
|
658
750
|
if (!cause && try_catch.HasCaught()) cause = RUNTIME_ERROR;
|
|
659
751
|
if (cause) result = v8::Undefined(st.isolate);
|
|
660
752
|
auto err = to_error(st, &try_catch, cause);
|
|
@@ -662,10 +754,21 @@ fail:
|
|
|
662
754
|
assert(try_catch.HasCaught());
|
|
663
755
|
goto fail; // retry; can be termination exception
|
|
664
756
|
}
|
|
757
|
+
restore_termination(st, preserve_termination);
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
extern "C" void v8_call(State *pst, const uint8_t *p, size_t n)
|
|
761
|
+
{
|
|
762
|
+
v8_call_impl(pst, p, n, false);
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
extern "C" void v8_call_await(State *pst, const uint8_t *p, size_t n)
|
|
766
|
+
{
|
|
767
|
+
v8_call_impl(pst, p, n, true);
|
|
665
768
|
}
|
|
666
769
|
|
|
667
770
|
// response is errback [result, err] array
|
|
668
|
-
|
|
771
|
+
void v8_eval_impl(State *pst, const uint8_t *p, size_t n, bool await)
|
|
669
772
|
{
|
|
670
773
|
State& st = *pst;
|
|
671
774
|
v8::TryCatch try_catch(st.isolate);
|
|
@@ -675,6 +778,14 @@ extern "C" void v8_eval(State *pst, const uint8_t *p, size_t n)
|
|
|
675
778
|
des.ReadHeader(st.context).Check();
|
|
676
779
|
v8::Local<v8::Value> result;
|
|
677
780
|
int cause = INTERNAL_ERROR;
|
|
781
|
+
bool preserve_termination = false;
|
|
782
|
+
bool nested = st.javascript_call_depth > 0;
|
|
783
|
+
JavascriptCallScope call_scope(st.javascript_call_depth);
|
|
784
|
+
if (await && nested) {
|
|
785
|
+
throw_nested_await_call(st);
|
|
786
|
+
cause = RUNTIME_ERROR;
|
|
787
|
+
goto fail;
|
|
788
|
+
}
|
|
678
789
|
{
|
|
679
790
|
v8::Local<v8::Value> request_v;
|
|
680
791
|
if (!des.ReadValue(st.context).ToLocal(&request_v)) goto fail;
|
|
@@ -694,16 +805,16 @@ extern "C" void v8_eval(State *pst, const uint8_t *p, size_t n)
|
|
|
694
805
|
cause = RUNTIME_ERROR;
|
|
695
806
|
auto maybe_result_v = script->Run(st.context);
|
|
696
807
|
if (!maybe_result_v.ToLocal(&result_v)) goto fail;
|
|
808
|
+
if (await && !await_promise(st, &result_v)) goto fail;
|
|
697
809
|
result = sanitize(st, result_v);
|
|
698
810
|
}
|
|
699
811
|
cause = NO_ERROR;
|
|
700
812
|
fail:
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
813
|
+
preserve_termination = suspend_termination(st, nested, cause);
|
|
814
|
+
if (bubble_up_ruby_exception(st, &try_catch)) {
|
|
815
|
+
restore_termination(st, preserve_termination);
|
|
816
|
+
return;
|
|
705
817
|
}
|
|
706
|
-
if (bubble_up_ruby_exception(st, &try_catch)) return;
|
|
707
818
|
if (!cause && try_catch.HasCaught()) cause = RUNTIME_ERROR;
|
|
708
819
|
if (cause) result = v8::Undefined(st.isolate);
|
|
709
820
|
auto err = to_error(st, &try_catch, cause);
|
|
@@ -711,6 +822,17 @@ fail:
|
|
|
711
822
|
assert(try_catch.HasCaught());
|
|
712
823
|
goto fail; // retry; can be termination exception
|
|
713
824
|
}
|
|
825
|
+
restore_termination(st, preserve_termination);
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
extern "C" void v8_eval(State *pst, const uint8_t *p, size_t n)
|
|
829
|
+
{
|
|
830
|
+
v8_eval_impl(pst, p, n, false);
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
extern "C" void v8_eval_await(State *pst, const uint8_t *p, size_t n)
|
|
834
|
+
{
|
|
835
|
+
v8_eval_impl(pst, p, n, true);
|
|
714
836
|
}
|
|
715
837
|
|
|
716
838
|
extern "C" void v8_heap_stats(State *pst)
|
|
@@ -775,6 +897,7 @@ extern "C" void v8_perform_microtask_checkpoint(State *pst)
|
|
|
775
897
|
// Leave any termination active so the enclosing v8_call/v8_eval frame
|
|
776
898
|
// surfaces OOM (set by v8_gc_callback) or watchdog termination to Ruby.
|
|
777
899
|
State& st = *pst;
|
|
900
|
+
JavascriptCallScope call_scope(st.javascript_call_depth);
|
|
778
901
|
v8::TryCatch try_catch(st.isolate);
|
|
779
902
|
try_catch.SetVerbose(st.verbose_exceptions);
|
|
780
903
|
v8::HandleScope handle_scope(st.isolate);
|
|
@@ -785,6 +908,8 @@ extern "C" void v8_perform_microtask_checkpoint(State *pst)
|
|
|
785
908
|
extern "C" void v8_pump_message_loop(State *pst)
|
|
786
909
|
{
|
|
787
910
|
State& st = *pst;
|
|
911
|
+
bool nested = st.javascript_call_depth > 0;
|
|
912
|
+
JavascriptCallScope call_scope(st.javascript_call_depth);
|
|
788
913
|
v8::TryCatch try_catch(st.isolate);
|
|
789
914
|
try_catch.SetVerbose(st.verbose_exceptions);
|
|
790
915
|
v8::HandleScope handle_scope(st.isolate);
|
|
@@ -800,7 +925,10 @@ extern "C" void v8_pump_message_loop(State *pst)
|
|
|
800
925
|
if (try_catch.HasCaught()) goto fail;
|
|
801
926
|
}
|
|
802
927
|
fail:
|
|
803
|
-
|
|
928
|
+
// A nested pump must leave termination active for the enclosing call/eval.
|
|
929
|
+
if (!nested &&
|
|
930
|
+
(st.terminate_requested.exchange(0) ||
|
|
931
|
+
st.isolate->IsExecutionTerminating())) {
|
|
804
932
|
st.isolate->CancelTerminateExecution();
|
|
805
933
|
st.err_reason = NO_ERROR;
|
|
806
934
|
}
|
|
@@ -914,7 +1042,8 @@ extern "C" void v8_snapshot(State *pst, const uint8_t *p, size_t n)
|
|
|
914
1042
|
}
|
|
915
1043
|
cause = NO_ERROR;
|
|
916
1044
|
fail:
|
|
917
|
-
if (st.
|
|
1045
|
+
if (st.terminate_requested.exchange(0) ||
|
|
1046
|
+
st.isolate->IsExecutionTerminating()) {
|
|
918
1047
|
st.isolate->CancelTerminateExecution();
|
|
919
1048
|
cause = st.err_reason ? st.err_reason : TERMINATED_ERROR;
|
|
920
1049
|
st.err_reason = NO_ERROR;
|
|
@@ -984,7 +1113,8 @@ extern "C" void v8_warmup(State *pst, const uint8_t *p, size_t n)
|
|
|
984
1113
|
}
|
|
985
1114
|
cause = NO_ERROR;
|
|
986
1115
|
fail:
|
|
987
|
-
if (st.
|
|
1116
|
+
if (st.terminate_requested.exchange(0) ||
|
|
1117
|
+
st.isolate->IsExecutionTerminating()) {
|
|
988
1118
|
st.isolate->CancelTerminateExecution();
|
|
989
1119
|
cause = st.err_reason ? st.err_reason : TERMINATED_ERROR;
|
|
990
1120
|
st.err_reason = NO_ERROR;
|
|
@@ -1008,10 +1138,42 @@ extern "C" void v8_low_memory_notification(State *pst)
|
|
|
1008
1138
|
pst->isolate->LowMemoryNotification();
|
|
1009
1139
|
}
|
|
1010
1140
|
|
|
1011
|
-
|
|
1012
|
-
|
|
1141
|
+
struct WakeupTask : public v8::Task
|
|
1142
|
+
{
|
|
1143
|
+
void Run() final {}
|
|
1144
|
+
};
|
|
1145
|
+
|
|
1146
|
+
// called from ruby or watchdog thread
|
|
1147
|
+
void request_termination(State *pst, unsigned request)
|
|
1013
1148
|
{
|
|
1149
|
+
pst->terminate_requested.fetch_or(request);
|
|
1014
1150
|
pst->isolate->TerminateExecution();
|
|
1151
|
+
// Wake await_promise if it may be blocked. A racing early return can leave
|
|
1152
|
+
// one harmless no-op task queued.
|
|
1153
|
+
if (pst->waiting_for_task.exchange(false)) {
|
|
1154
|
+
platform->GetForegroundTaskRunner(pst->isolate)
|
|
1155
|
+
->PostTask(std::make_unique<WakeupTask>());
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
extern "C" void v8_terminate_execution(State *pst)
|
|
1160
|
+
{
|
|
1161
|
+
request_termination(pst, NON_WATCHDOG_TERMINATION);
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
extern "C" void v8_terminate_watchdog(State *pst)
|
|
1165
|
+
{
|
|
1166
|
+
request_termination(pst, WATCHDOG_TERMINATION);
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
extern "C" void v8_cancel_watchdog_termination(State *pst)
|
|
1170
|
+
{
|
|
1171
|
+
unsigned requests = pst->terminate_requested.fetch_and(~WATCHDOG_TERMINATION);
|
|
1172
|
+
if (requests == WATCHDOG_TERMINATION) {
|
|
1173
|
+
pst->isolate->CancelTerminateExecution();
|
|
1174
|
+
if (pst->terminate_requested.load())
|
|
1175
|
+
pst->isolate->TerminateExecution();
|
|
1176
|
+
}
|
|
1015
1177
|
}
|
|
1016
1178
|
|
|
1017
1179
|
// called from ruby thread
|
|
@@ -1019,6 +1181,7 @@ extern "C" void v8_cancel_terminate_execution(State *pst)
|
|
|
1019
1181
|
{
|
|
1020
1182
|
// TerminateExecution can race with V8 completing and queue a termination
|
|
1021
1183
|
// for the next entry without IsExecutionTerminating() becoming true.
|
|
1184
|
+
pst->terminate_requested.store(0);
|
|
1022
1185
|
pst->isolate->CancelTerminateExecution();
|
|
1023
1186
|
}
|
|
1024
1187
|
|
|
@@ -39,7 +39,9 @@ struct State *v8_thread_init(struct Context *c, const uint8_t *snapshot_buf,
|
|
|
39
39
|
int verbose_exceptions); // calls v8_thread_main
|
|
40
40
|
void v8_attach(struct State *pst, const uint8_t *p, size_t n);
|
|
41
41
|
void v8_call(struct State *pst, const uint8_t *p, size_t n);
|
|
42
|
+
void v8_call_await(struct State *pst, const uint8_t *p, size_t n);
|
|
42
43
|
void v8_eval(struct State *pst, const uint8_t *p, size_t n);
|
|
44
|
+
void v8_eval_await(struct State *pst, const uint8_t *p, size_t n);
|
|
43
45
|
void v8_heap_stats(struct State *pst);
|
|
44
46
|
void v8_heap_snapshot(struct State *pst);
|
|
45
47
|
void v8_perform_microtask_checkpoint(struct State *pst);
|
|
@@ -47,7 +49,9 @@ void v8_pump_message_loop(struct State *pst);
|
|
|
47
49
|
void v8_snapshot(struct State *pst, const uint8_t *p, size_t n);
|
|
48
50
|
void v8_warmup(struct State *pst, const uint8_t *p, size_t n);
|
|
49
51
|
void v8_low_memory_notification(struct State *pst);
|
|
50
|
-
void v8_terminate_execution(struct State *pst); // called from ruby
|
|
52
|
+
void v8_terminate_execution(struct State *pst); // called from ruby thread
|
|
53
|
+
void v8_terminate_watchdog(struct State *pst); // called from watchdog thread
|
|
54
|
+
void v8_cancel_watchdog_termination(struct State *pst); // called from v8 thread
|
|
51
55
|
void v8_cancel_terminate_execution(struct State *pst); // called from ruby thread
|
|
52
56
|
void v8_single_threaded_enter(struct State *pst, struct Context *c, void (*f)(struct Context *c));
|
|
53
57
|
void v8_single_threaded_dispose(struct State *pst);
|
|
@@ -243,33 +243,38 @@ static void ser_num(Ser *s, double v)
|
|
|
243
243
|
}
|
|
244
244
|
}
|
|
245
245
|
|
|
246
|
-
// ser_bigint: |
|
|
247
|
-
|
|
246
|
+
// ser_bigint: |p| points to |n| bytes, interpreted as little-endian
|
|
247
|
+
// 64-bit words. Keep the interface byte-oriented so callers don't need to
|
|
248
|
+
// expose a concrete word type.
|
|
249
|
+
static void ser_bigint(Ser *s, const void *p, size_t n, int sign)
|
|
248
250
|
{
|
|
251
|
+
const uint8_t *bytes;
|
|
252
|
+
|
|
249
253
|
if (*s->err)
|
|
250
254
|
return;
|
|
251
255
|
if (n % 8) {
|
|
252
256
|
snprintf(s->err, sizeof(s->err), "bad bigint");
|
|
253
257
|
return;
|
|
254
258
|
}
|
|
259
|
+
bytes = p;
|
|
255
260
|
w_byte(s, 'Z');
|
|
256
261
|
// chop off high all-zero words
|
|
257
|
-
n
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
break;
|
|
261
|
-
if (n == (size_t)-1) {
|
|
262
|
+
while (n > 0 && bytes[n-1] == 0)
|
|
263
|
+
n--;
|
|
264
|
+
if (n == 0) {
|
|
262
265
|
w_byte(s, 0); // normalized zero
|
|
263
266
|
} else {
|
|
264
|
-
n =
|
|
267
|
+
n = (n + 7) & ~(size_t)7;
|
|
265
268
|
w_varint(s, 2*n + (sign < 0));
|
|
266
|
-
w(s,
|
|
269
|
+
w(s, bytes, n);
|
|
267
270
|
}
|
|
268
271
|
}
|
|
269
272
|
|
|
270
273
|
static void ser_int(Ser *s, int64_t v)
|
|
271
274
|
{
|
|
275
|
+
uint8_t bytes[8];
|
|
272
276
|
uint64_t t;
|
|
277
|
+
size_t i;
|
|
273
278
|
int sign;
|
|
274
279
|
|
|
275
280
|
if (*s->err)
|
|
@@ -279,8 +284,10 @@ static void ser_int(Ser *s, int64_t v)
|
|
|
279
284
|
if (v <= INT64_MAX/1024)
|
|
280
285
|
return ser_num(s, v);
|
|
281
286
|
t = v < 0 ? (uint64_t)(-(v + 1)) + 1 : (uint64_t)v;
|
|
287
|
+
for (i = 0; i < sizeof(bytes); i++)
|
|
288
|
+
bytes[i] = t >> (8*i);
|
|
282
289
|
sign = v < 0 ? -1 : 1;
|
|
283
|
-
ser_bigint(s,
|
|
290
|
+
ser_bigint(s, bytes, sizeof(bytes), sign);
|
|
284
291
|
} else {
|
|
285
292
|
w_byte(s, 'I');
|
|
286
293
|
w_zigzag(s, v);
|