forked from rebolsource/r3
-
Notifications
You must be signed in to change notification settings - Fork 27
/
sys-value.h
executable file
·2489 lines (2102 loc) · 93.3 KB
/
sys-value.h
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
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
//
// Rebol 3 Language Interpreter and Run-time Environment
// "Ren-C" branch @ https://github.com/metaeducation/ren-c
//
// Copyright 2012 REBOL Technologies
// Copyright 2012-2016 Rebol Open Source Contributors
// REBOL is a trademark of REBOL Technologies
//
// See README.md and CREDITS.md for more information
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//=////////////////////////////////////////////////////////////////////////=//
//
// Summary: Definitions for the Rebol Value Struct (REBVAL) and Helpers
// File: %sys-value.h
//
//=////////////////////////////////////////////////////////////////////////=//
//
// REBVAL is the structure/union for all Rebol values. It's designed to be
// four C pointers in size (so 16 bytes on 32-bit platforms and 32 bytes
// on 64-bit platforms). Operation will be most efficient with those sizes,
// and there are checks on boot to ensure that `sizeof(REBVAL)` is the
// correct value for the platform. But from a mechanical standpoint, the
// system should be *able* to work even if the size is different.
//
// Of the four 32-or-64-bit slots that each value has, the first is used for
// the value's "Header". This includes the data type, such as REB_INTEGER,
// REB_BLOCK, REB_STRING, etc. Then there are 8 flags which are for general
// purposes that could apply equally well to any type of value (including
// whether the value should have a new-line after it when molded out inside
// of a block). There are 8 bits which are custom to each type--for
// instance whether a key in an object is hidden or not. Then there are
// 8 bits currently reserved for future use.
//
// The remaining content of the REBVAL struct is the "Payload". It is the
// size of three (void*) pointers, and is used to hold whatever bits that
// are needed for the value type to represent itself. Perhaps obviously,
// an arbitrarily long string will not fit into 3*32 bits, or even 3*64 bits!
// You can fit the data for an INTEGER or DECIMAL in that (at least until
// they become arbitrary precision) but it's not enough for a generic BLOCK!
// or a FUNCTION! (for instance). So those pointers are used to point to
// things, and often they will point to one or more Rebol Series (see
// %sys-series.h for an explanation of REBSER, REBARR, REBCTX, and REBMAP.)
//
// While some REBVALs are in C stack variables, most reside in the allocated
// memory block for a Rebol series. The memory block for a series can be
// resized and require a reallocation, or it may become invalid if the
// containing series is garbage-collected. This means that many pointers to
// REBVAL are unstable, and could become invalid if arbitrary user code
// is run...this includes values on the data stack, which is implemented as
// a series under the hood. (See %sys-stack.h)
//
// A REBVAL in a C stack variable does not have to worry about its memory
// address becoming invalid--but by default the garbage collector does not
// know that value exists. So while the address may be stable, any series
// it has in the payload might go bad. Use PUSH_GUARD_VALUE() to protect a
// stack variable's payload, and then DROP_GUARD_VALUE() when the protection
// is not needed. (You must always drop the last guard pushed.)
//
// For a means of creating a temporary array of GC-protected REBVALs, see
// the "chunk stack" in %sys-stack.h. This is used when building function
// argument frames, which means that the REBVAL* arguments to a function
// accessed via ARG() will be stable as long as the function is running.
//
//
// Note: Forward declarations are in %reb-defs.h
//
#ifndef VALUE_H
#define VALUE_H
// A `#pragma pack` of 4 was requested by the R3-Alpha source for the
// duration of %sys-value.h:
//
// http://stackoverflow.com/questions/3318410/
//
// Pushed here and popped at the end of the file to the previous value
//
// !!! Compilers are free to ignore pragmas (or error on them), and this can
// cause a loss of performance...so it might not be worth doing. Especially
// because the ordering of fields in REBVAL members is alignment-conscious
// already. Since Rebol series indexes (REBINT) and length counts (REBCNT)
// are still 32-bits on 64-bit platforms, it means that often REBINTs are
// "paired up" in structures to create a 64-bit alignment for a pointer
// that comes after them. So everything is pretty well aligned as-is.
//
#pragma pack(push,4)
//=////////////////////////////////////////////////////////////////////////=//
//
// VALUE HEADER (`struct Reb_Value_Header`)
//
//=////////////////////////////////////////////////////////////////////////=//
//
// The layout of the header corresponds to the following bitfield
// structure on big endian machines:
//
// unsigned specific:16; // flags that can apply to any REBVAL kind
// unsigned general:8; // flags that can apply to any kind of REBVAL
// unsigned kind:6; // underlying system datatype (64 kinds)
// unsigned settable:1; // for debug build only--"formatted" to write
// unsigned not_end:1; // not an end marker
//
// Due to a desire to be able to assign all the header bits in one go
// with a native-platform-sized int, this is done with bit masking.
// Using bitfields would bring in questions of how smart the
// optimizer is, as well as the endianness of the underlyling machine.
//
// We use REBUPT (Rebol's pre-C99 compatibility type for uintptr_t,
// which is just uintptr_t from C99 on). Only the low 32 bits are used
// on 64-bit machines in order to make sure all the features work on
// 32-bit machines...but could be used for some optimization or caching
// purpose to enhance the 64-bit build. No such uses implemented yet.
//
struct Reb_Value_Header {
REBUPT bits;
};
// `NOT_END_MASK`
//
// If set, it means this is *not* an end marker. The bit has been picked
// strategically to be in the negative sense, and in the lowest bit position.
// This means that any even-valued unsigned integer REBUPT value can be used
// to implicitly signal an end.
//
// If this bit is 0, it means that *no other header bits are valid*, as it
// may contain arbitrary data used for non-REBVAL purposes.
//
// Note that the value doing double duty as a number for one purpose and an
// END marker as another *must* be another REBUPT. It cannot be a pointer
// (despite being guaranteed-REBUPT-sized, and despite having a value that
// is 0 when you mod it by 2. So-called "type-punning" is unsafe with a
// likelihood of invoking "undefined behavior", while it's the compiler's
// responsibility to guarantee that pointers to memory of the same type of
// data be compatibly read-and-written.
//
#define NOT_END_MASK cast(REBUPT, 0x01)
#define GENERAL_VALUE_BIT 8
#define TYPE_SPECIFIC_BIT 16
// `WRITABLE_MASK_DEBUG`
//
// This is for the debug build, to make it safer to use the implementation
// trick of NOT_END_MASK. It indicates the slot is "REBVAL sized", and can
// be written into--including to be written with SET_END().
//
// It's again a strategic choice--the 2nd lowest bit and in the negative.
// This means any REBUPT value whose % 4 within a container doing
// double-duty as an implicit terminator for the contained values can
// trigger an alert if the values try to overwrite it.
//
#ifdef NDEBUG
//
// Though in the abstract it is desirable to have a way to protect an
// individual value from writing even in the release build, this is
// a debug-only check...it makes every value header initialization have
// to do two writes instead of one (one to format, then later to write
// and check that it is properly formatted space)
//
#else
// We want to be assured that we are not trying to take the type of a
// value that is actually an END marker, because end markers chew out only
// one bit--the rest of the REBUPT bits besides the bottom two may be
// anything necessary for the purpose.
//
#define WRITABLE_MASK_DEBUG cast(REBUPT, 0x02)
#endif
// The type mask comes up a bit and it's a fairly obvious constant, so this
// hardcodes it for obviousness. High 6 bits of the lowest byte.
//
#define HEADER_TYPE_MASK cast(REBUPT, 0xFC)
//=////////////////////////////////////////////////////////////////////////=//
//
// END marker (not a value type, only writes `struct Reb_Value_Flags`)
//
//=////////////////////////////////////////////////////////////////////////=//
//
// Historically Rebol arrays were always one value longer than their maximum
// content, and this final slot was used for a special REBVAL called END!.
// Like a null terminator in a C string, it was possible to start from one
// point in the series and traverse to find the end marker without needing
// to maintain a count. Rebol series store their length also--but it's
// faster and more general to use the terminator.
//
// Ren-C changed this so that end is not a data type, but rather seeing a
// header slot with the lowest bit set to 0. (See NOT_END_MASK for
// an explanation of this choice.) The upshot is that a data structure
// designed to hold Rebol arrays is able to terminate an array at full
// capacity with a pointer-sized integer with the lowest 2 bits clear, and
// use the rest of the bits for other purposes. (See WRITABLE_MASK_DEBUG
// for why it's the low 2 bits and not just the lowest bit.)
//
// This means not only is a full REBVAL not needed to terminate, the sunk cost
// of an existing 32-bit or 64-bit number (depending on platform) can be used
// to avoid needing even 1/4 of a REBVAL for a header to terminate. (See the
// `size` field in `struct Reb_Chunk` from %sys-stack.h for a simple example
// of the technique.)
//
// !!! Because Rebol Arrays (REBARR) have both a length and a terminator, it
// is important to keep these in sync. R3-Alpha sought to give code the
// freedom to work with unterminated arrays if the cost of writing terminators
// was not necessary. Ren-C pushed back against this to try and be more
// uniform to get the invariants under control. A formal balance is still
// being sought of when terminators will be required and when they will not.
//
#ifdef NDEBUG
#define IS_END(v) LOGICAL((v)->header.bits % 2 == 0)
#else
#define IS_END(v) IS_END_Debug(v)
#endif
#define NOT_END(v) NOT(IS_END(v))
#ifdef NDEBUG
#define SET_END(v) ((v)->header.bits = 0)
#else
//
// The slot we are trying to write into must have at least been formatted
// in the debug build VAL_INIT_WRITABLE_DEBUG(). Otherwise it could be a
// pointer with its low bit clear, doing double-duty as an IS_END(),
// marker...which we cannot overwrite...not even with another END marker.
//
#define SET_END(v) \
(Assert_REBVAL_Writable((v), __FILE__, __LINE__), \
(v)->header.bits = WRITABLE_MASK_DEBUG | REB_MAX)
#endif
// Pointer to a global END marker. Though this global value is allocated to
// the size of a REBVAL, only the header is initialized. This means if any
// of the value payload is accessed, it will trip memory checkers like
// Valgrind or Address Sanitizer to warn of the mistake.
//
#define END_VALUE (&PG_End_Val)
//=////////////////////////////////////////////////////////////////////////=//
//
// GENERAL FLAGS common to every REBVAL
//
//=////////////////////////////////////////////////////////////////////////=//
//
// The value option flags are 8 individual bitflags which apply to every
// value of every type. Due to their scarcity, they are chosen carefully.
//
enum {
// `VALUE_FLAG_FALSE`
//
// Both NONE! and LOGIC!'s false state are FALSE? ("conditionally false").
// All other types are TRUE?. To make checking FALSE? and TRUE? faster,
// this bit is set when creating NONE! or FALSE. As a result, LOGIC!
// does not need to store any data in its payload... its data of being
// true or false is already covered by this header bit.
//
VALUE_FLAG_FALSE = 1 << (GENERAL_VALUE_BIT + 0),
// `VALUE_FLAG_LINE`
//
// If the line marker bit is 1, then when the value is molded it will put
// a newline before the value. The logic is a bit more subtle than that,
// because an ANY-PATH! could not be LOADed back if this were allowed.
// The bit is set initially by what the scanner detects, and then left
// to the user's control after that.
//
// !!! The native `new-line` is used set this, which has a somewhat
// poor name considering its similarity to `newline` the line feed char.
//
VALUE_FLAG_LINE = 1 << (GENERAL_VALUE_BIT + 1),
// `VALUE_FLAG_THROWN`
//
// The thrown bit is being phased out, as the concept of a value itself
// being "thrown" does not make a lot of sense, compared to the idea
// that the evaluator itself is in a "throwing state". If a thrown bit
// can get on a value, then one has to worry about that value getting
// copied and showing up somewhere that it doesn't make sense.
//
// Originally, R3-Alpha did not have a thrown bit on values, rather the
// throw itself was represented as a certain kind of ERROR! value. Ren-C
// modifications extended THROW to allow a /NAME that could be a full
// REBVAL (instead of a selection from a limited set of words). This
// made it possible to identify a throw by an object, function,
// fully bound word, etc.
//
// But even after the change, the "thrown-ness" was still a property
// of the "throw-name REBVAL". By virtue of being a property on a
// value *it could be dropped on the floor and ignored*. There were
// countless examples of this originating in the code.
//
// As part of the process of stamping out the idea that thrownness comes
// from a value, all routines that can potentially return thrown values
// have been adapted to return a boolean and adopt the XXX_Throws()
// naming convention, so one can write:
//
// if (XXX_Throws()) {
// /* handling code */
// }
//
// This forced every caller to consciously have a code path dealing with
// potentially thrown values, reigning in the previous problems. At
// time of writing, the situation is much more under control, and natives
// return a flag indicating that they wish to throw a value vs. return
// one. This is checked redundantly against the value bit for now, but
// it is likely that the bit will be removed in favor of pushing the
// responsibility into the evaluator state.
//
VALUE_FLAG_THROWN = 1 << (GENERAL_VALUE_BIT + 2),
// This is a bit used in conjunction with VALUE_FLAG_THROWN, which could
// also be folded in to be a model of being in an "exiting state". The
// usage is for definitionally scoped RETURN, RESUME/AT, and EXIT/FROM
// where the frame desired to be targeted is marked with this flag.
// Currently it is indicated by either the object of the call frame (for
// a CLOSURE!) or the paramlist for all other ANY-FUNCTION!.
//
// !!! WARNING - In the current scheme this will only jump up to the most
// recent instantiation of the function, as it does not have unique
// identity in relative binding. When FUNCTION! and its kind are updated
// to use a new technique that brings it to parity with CLOSURE! in this
// regard, then that will fix this.
//
VALUE_FLAG_EXIT_FROM = 1 << (GENERAL_VALUE_BIT + 3),
// `VALUE_FLAG_RELATIVE` is used to indicate a value that needs to have
// a specific context added into it before it can have its bits copied
// or used for some purposes. An ANY-WORD! is relative if it refers to
// a local or argument of a function, and has its bits resident in the
// deep copy of that function's body. An ANY-ARRAY! in the deep copy
// of a function body must be relative also to the same function if
// it contains any instances of such relative words.
//
// !!! The feature of specific binding is a work in progress, and only
// bits of the supporting implementation changes are committed into the
// master branch at a time.
//
VALUE_FLAG_RELATIVE = 1 << (GENERAL_VALUE_BIT + 4)
};
// VALUE_FLAG_XXX flags are applicable to all types. Type-specific flags are
// named things like TYPESET_FLAG_XXX or WORD_FLAG_XXX and only apply to the
// type that they reference. Both use these XXX_VAL_FLAG accessors.
//
#ifdef NDEBUG
#define SET_VAL_FLAG(v,f) \
((v)->header.bits |= (f))
#define GET_VAL_FLAG(v,f) \
LOGICAL((v)->header.bits & (f))
#define CLEAR_VAL_FLAG(v,f) \
((v)->header.bits &= ~cast(REBUPT, (f)))
#else
// For safety in the debug build, all the type-specific flags include
// their type as part of the flag. This type is checked first, and then
// masked out to use the single-bit-flag value which is intended. The
// check uses the bits of an exemplar type to identify the category
// (e.g. REB_FUNCTION for ANY-FUNCTION!, REB_OBJECT for ANY-CONTEXT!)
#define SET_VAL_FLAG(v,f) \
(Assert_Flags_Are_For_Value((v), (f)), \
(v)->header.bits |= ((f) & ~HEADER_TYPE_MASK))
#define GET_VAL_FLAG(v,f) \
(Assert_Flags_Are_For_Value((v), (f)), \
LOGICAL((v)->header.bits & ((f) & ~HEADER_TYPE_MASK)))
#define CLEAR_VAL_FLAG(v,f) \
(Assert_Flags_Are_For_Value((v), (f)), \
(v)->header.bits &= ~cast(REBUPT, (f) & ~HEADER_TYPE_MASK))
#endif
//
// Setting and clearing multiple flags works, so these names make that "clear"
//
#define SET_VAL_FLAGS(v,f) \
SET_VAL_FLAG((v), (f))
#define CLEAR_VAL_FLAGS(v,f) \
CLEAR_VAL_FLAG((v), (f))
//=////////////////////////////////////////////////////////////////////////=//
//
// VALUE "KIND" (1 out of 64 different foundational types)
//
//=////////////////////////////////////////////////////////////////////////=//
//
// Every value has 6 bits reserved for its VAL_TYPE(). The reason only 6
// are used is because low-level TYPESET!s are only 64-bits (so they can fit
// into a REBVAL payload, along with a key symbol to represent a function
// parameter). If there were more types, they couldn't be flagged in a
// typeset that fit in a REBVAL under that constraint.
//
// VAL_TYPE() should obviously not be called on uninitialized memory. But
// it should also not be called on an END marker, as those markers only
// guarantee the low bit as having Rebol-readable-meaning. In debug builds,
// this is asserted by VAL_TYPE_Debug.
//
#ifdef NDEBUG
#define VAL_TYPE(v) \
cast(enum Reb_Kind, (v)->header.bits & HEADER_TYPE_MASK)
#else
#define VAL_TYPE(v) \
VAL_TYPE_Debug((v), __FILE__, __LINE__)
#endif
// For the processor's convenience, the 64 basic Rebol types are shifted to
// the left by 2 bits. This makes their range go from 0..251 instead of
// from 0..63. The choice to do this is because most of the type the types
// are just used in comparisons or switch statements, and it's cheaper to
// not have to shift out the 2 low bits that are used for END and WRITABLE
// flags in the header most of the time.
//
// However, to index into a zero based array with 64 elements, the shift
// needs to be done. If that's required these defines adjust for the shift.
// See also REB_MAX_0
//
#define KIND_FROM_0(z) cast(enum Reb_Kind, (z) << 2)
#define TO_0_FROM_KIND(k) (cast(REBCNT, (k)) >> 2)
#define VAL_TYPE_0(v) TO_0_FROM_KIND(VAL_TYPE(v))
// SET_TYPE_BITS only sets the type, with other header bits intact. This
// should be used when you are sure that the new type payload is in sync with
// the type and bits (for instance, changing from one ANY-WORD! type to
// another, the binding needs to be in sync with the header bits)
//
// NOTE: The header MUST already be valid and initialized to use this! For
// fresh value creation, one wants to use VAL_RESET_HEADER to clear bits and
// set the type.
//
// !!! Is it worth the effort to add a debugging flag into the value to
// disallow calling this routine if VAL_RESET_HEADER has not been run, or
// are there too few instances to be worth it and is _BITS enough a hint?
//
#define VAL_SET_TYPE_BITS(v,t) \
((v)->header.bits &= ~cast(REBUPT, HEADER_TYPE_MASK), \
(v)->header.bits |= (t))
// VAL_RESET_HEADER clears out the header and sets it to a new type (and also
// sets the option bits indicating the value is *not* an END marker, and
// that the value is a full cell which can be written to).
//
#ifdef NDEBUG
#define VAL_RESET_HEADER(v,t) \
((v)->header.bits = NOT_END_MASK | (t))
#else
// The debug build includes an extra check that the value we are about
// to write the header of is actually a full REBVAL-sized slot...and not
// just an implicit END marker that's really doing double-duty as some
// internal pointer of a container structure.
//
#define VAL_RESET_HEADER(v,t) \
(Assert_REBVAL_Writable((v), __FILE__, __LINE__), \
(v)->header.bits = NOT_END_MASK | WRITABLE_MASK_DEBUG | (t))
#endif
// !!! SET_ZEROED is a macro-capture of a dodgy behavior of R3-Alpha,
// which was to assume that clearing the payload of a value and then setting
// the header made it the `zero?` of that type. Review uses.
//
#define SET_ZEROED(v,t) \
(VAL_RESET_HEADER((v),(t)), \
CLEAR(&(v)->payload, sizeof(union Reb_Value_Payload)))
//=////////////////////////////////////////////////////////////////////////=//
//
// TRACK payload (not a value type, only in DEBUG)
//
//=////////////////////////////////////////////////////////////////////////=//
//
// `struct Reb_Track` is the value payload in debug builds for any REBVAL
// whose VAL_TYPE() doesn't need any information beyond the header. This
// offers a chance to inject some information into the payload to help
// know where the value originated. It is used by TRASH!, UNSET!, NONE!,
// LOGIC!, and BAR!.
//
// In addition to the file and line number where the assignment was made,
// the "tick count" of the DO loop is also saved. This means that it can
// be possible in a repro case to find out which evaluation step produced
// the value--and at what place in the source. Repro cases can be set to
// break on that tick count, if it is deterministic.
//
// !!! If we're not using TRACK_EMPTY_PAYLOADS, should this POISON_MEMORY()
// on the payload to catch invalid reads? Trash values don't hang around
// that long, except for the values in the extra "->rest" capacity of series.
// Would that be too many memory poisonings to handle efficiently?
//
#define TRACK_EMPTY_PAYLOADS
#if !defined(NDEBUG)
#ifdef TRACK_EMPTY_PAYLOADS
struct Reb_Track {
const char *filename;
int line;
REBCNT count;
};
#define SET_TRACK_PAYLOAD(v) \
( \
(v)->payload.track.filename = __FILE__, \
(v)->payload.track.line = __LINE__, \
(v)->payload.track.count = TG_Do_Count, \
NOOP \
)
#define VAL_TRACK_FILE(v) ((v)->payload.track.filename)
#define VAL_TRACK_LINE(v) ((v)->payload.track.line)
#define VAL_TRACK_COUNT(v) ((v)->payload.track.count)
#else
#define SET_TRACK_PAYLOAD(v) NOOP
#endif
#endif
//=////////////////////////////////////////////////////////////////////////=//
//
// TRASH! (may use `struct Reb_Track`)
//
//=////////////////////////////////////////////////////////////////////////=//
//
// NOTE: In debug builds, Reb_Trash saves the line and file where
// the "trashing" of the cell slot happened See `Reb_Value.track`.
//
// Trash is what's written into cells in the debug build when the cell is
// expected to be overwitten with valid data. To prevent it from being
// inspected while it's in an invalid state, VAL_TYPE used on a trash value
// will assert in the debug build.
//
// By default, the garbage collector will raise an alert if a TRASH! value
// is not overwritten by the time it sees it. But some cases work with
// GC-visible locations and want the GC to ignore a transitional trash. For
// these cases use SET_TRASH_GC_SAFE().
//
// IS_TRASH_DEBUG() can be used to test for trash, but in debug builds only.
// The macros for setting trash will compile in both debug and release builds,
// though an unsafe trash will be a NOOP in release builds. (So the "trash"
// will be uninitialized memory, in that case.) A safe trash set turns into
// a SET_UNSET() in release builds.
//
#ifdef NDEBUG
#define MARK_VAL_UNWRITABLE_DEBUG(v) NOOP
#define VAL_INIT_WRITABLE_DEBUG(v) NOOP
#define SET_TRASH_IF_DEBUG(v) NOOP
#define SET_TRASH_SAFE(v) SET_UNSET(v)
#else
enum {
// GC safe trash (UNSET! in release build)
//
TRASH_FLAG_SAFE = (1 << TYPE_SPECIFIC_BIT) | REB_TRASH
};
// Special type check...we don't want to use a VAL_TYPE() == REB_TRASH
// because VAL_TYPE is supposed to assert on trash
//
#define IS_TRASH_DEBUG(v) \
(((v)->header.bits & HEADER_TYPE_MASK) == 0)
// This particularly virulent form of trashing will make the resultant
// cell unable to be used with SET_END() or VAL_RESET_HEADER() until
// a SET_TRASH_IF_DEBUG() or SET_TRASH_SAFE() is used to overrule it.
//
#define MARK_VAL_UNWRITABLE_DEBUG(v) \
((v)->header.bits &= ~cast(REBUPT, WRITABLE_MASK_DEBUG), NOOP)
// The debug build requires that any value slot that's going to be written
// to via VAL_RESET_HEADER() be marked writable. Series and other value
// containers do this automatically, but if you make a REBVAL as a stack
// variable then it will have to be done before any write can happen.
//
// An alignment check to the size of a pointer is commented out due to
// being expensive to run on every value in the system, but should be
// added under an "intense checks" switch.
//
#define VAL_INIT_WRITABLE_DEBUG(v) \
( \
/* assert(cast(REBUPT, (v)) % sizeof(REBUPT) == 0), */ \
(v)->header.bits = NOT_END_MASK | WRITABLE_MASK_DEBUG, \
SET_TRACK_PAYLOAD(v) \
)
#define SET_TRASH_IF_DEBUG(v) \
( \
VAL_RESET_HEADER((v), REB_TRASH), \
SET_TRACK_PAYLOAD(v) \
)
#define SET_TRASH_SAFE(v) \
( \
VAL_RESET_HEADER((v), REB_TRASH), \
SET_VAL_FLAG((v), TRASH_FLAG_SAFE), \
SET_TRACK_PAYLOAD(v) \
)
#endif
//=////////////////////////////////////////////////////////////////////////=//
//
// UNSET! (unit type - fits in header bits, may use `struct Reb_Track`)
//
//=////////////////////////////////////////////////////////////////////////=//
//
// Unset is one of Rebol's two unit types (the other being NONE!). Whereas
// integers can be values like 1, 2, 3... and LOGIC! can be true or false,
// an UNSET! can be only... unset. Its type is its value.
//
// By default, variable lookups that come back as unset will result in an
// error, and the average user would not be putting them into blocks on
// purpose. Hence the Ren-C branch of Rebol3 pushed forward an initiative
// to make unsets frequently denote an intent to "opt out" of things. This
// involved
//
// Since they have no data payload, unsets in the debug build use Reb_Track
// as their structure to show where-and-when they were assigned. This is
// helpful because unset is the default initialization value for several
// constructs.
//
#ifdef NDEBUG
#define SET_UNSET(v) \
VAL_RESET_HEADER((v), REB_UNSET)
#else
#define SET_UNSET(v) \
(VAL_RESET_HEADER((v), REB_UNSET), SET_TRACK_PAYLOAD(v))
#endif
// Pointer to a global protected unset value that can be used when a read-only
// UNSET! value is needed.
//
#define UNSET_VALUE (&PG_Unset_Value[0])
// In legacy mode Ren-C still supports the old convention that IFs that don't
// take the true branch or a WHILE loop that never runs a body return a NONE!
// value instead of an UNSET!. To track the locations where this decision is
// made more easily, SET_UNSET_UNLESS_LEGACY_NONE() is used.
//
#ifdef NDEBUG
#define SET_UNSET_UNLESS_LEGACY_NONE(v) \
SET_UNSET(v) // LEGACY() only available in Debug builds
#else
#define SET_UNSET_UNLESS_LEGACY_NONE(v) \
(LEGACY(OPTIONS_NONE_INSTEAD_OF_UNSETS) ? SET_NONE(v) : SET_UNSET(v))
#endif
//=////////////////////////////////////////////////////////////////////////=//
//
// BAR! and LIT-BAR! (fits in header bits, may use `struct Reb_Track`)
//
//=////////////////////////////////////////////////////////////////////////=//
//
// The "expression barrier" is denoted by a lone vertical bar `|`, and it
// has the special property of being rejected for argument fulfillment, but
// ignored at interstitial positions. So:
//
// append [a b c] | [d e f] print "Hello" ;-- will cause an error
// append [a b c] [d e f] | print "Hello" ;-- is legal
//
// This makes it similar to an UNSET! in behavior, but given its specialized
// purpose unlikely to conflict as being needed to be passed as an actual
// parameter. Literal unsets in source are treated differently by the
// evaluator than unsets in variables or coming from the result of a function
// call, so that `append [a b c] something-that-returns-a-bar` is legal.
//
// The other loophole for making barriers is the LIT-BAR!, which can allow
// passing a BAR! by value. So `append [a b c] '|` would work.
//
#ifdef NDEBUG
#define SET_BAR(v) \
VAL_RESET_HEADER((v), REB_BAR)
#define SET_LIT_BAR(v) \
VAL_RESET_HEADER((v), REB_LIT_BAR)
#else
#define SET_BAR(v) \
(VAL_RESET_HEADER((v), REB_BAR), SET_TRACK_PAYLOAD(v))
#define SET_LIT_BAR(v) \
(VAL_RESET_HEADER((v), REB_LIT_BAR), SET_TRACK_PAYLOAD(v))
#endif
//=////////////////////////////////////////////////////////////////////////=//
//
// NONE! (unit type - fits in header bits, may use `struct Reb_Track`)
//
//=////////////////////////////////////////////////////////////////////////=//
//
// Unlike UNSET!, none values are inactive. They do not cause errors
// when they are used in situations like the condition of an IF statement.
// Instead they are considered to be false--like the LOGIC! #[false] value.
// So none is considered to be the other "conditionally false" value.
//
// Only those two values are conditionally false in Rebol, and testing for
// conditional truth and falsehood is frequent. Hence in addition to its
// type, NONE! also carries a header bit that can be checked for conditional
// falsehood.
//
// Though having tracking information for a none is less frequently useful
// than for TRASH! or UNSET!, it's there in debug builds just in case it
// ever serves a purpose, as the REBVAL payload is not being used.
//
#ifdef NDEBUG
#define SET_NONE(v) \
((v)->header.bits = VALUE_FLAG_FALSE | NOT_END_MASK | REB_NONE)
#else
#define SET_NONE(v) \
(Assert_REBVAL_Writable((v), __FILE__, __LINE__), \
(v)->header.bits = VALUE_FLAG_FALSE | \
NOT_END_MASK | WRITABLE_MASK_DEBUG | REB_NONE, \
SET_TRACK_PAYLOAD(v))
#endif
#define NONE_VALUE (&PG_None_Value[0])
//=////////////////////////////////////////////////////////////////////////=//
//
// LOGIC! (type and value fits in header bits, may use `struct Reb_Track`)
//
//=////////////////////////////////////////////////////////////////////////=//
//
// A logic can be either true or false. For purposes of optimization, logical
// falsehood is indicated by one of the value option bits in the header--as
// opposed to in the value payload. This means it can be tested quickly and
// that a single check can test for both NONE! and logic false.
//
// Conditional truth and falsehood allows an interpretation where a NONE!
// is a "falsey" value as well as logic false. Unsets are neither
// conditionally true nor conditionally false, and so debug builds will
// complain if you try to determine which it is. (It likely means a mistake
// was made in skipping a formal decision-point regarding whether an unset
// should represent an "opt out" or an error.)
//
// Due to no need for payload, it goes ahead and includes a TRACK payload
// in debug builds.
//
#ifdef NDEBUG
#define SET_TRUE(v) \
((v)->header.bits = REB_LOGIC | NOT_END_MASK)
#define SET_FALSE(v) \
((v)->header.bits = REB_LOGIC | NOT_END_MASK \
| VALUE_FLAG_FALSE)
#else
#define SET_TRUE(v) \
(Assert_REBVAL_Writable((v), __FILE__, __LINE__), \
(v)->header.bits = REB_LOGIC | NOT_END_MASK \
| WRITABLE_MASK_DEBUG, \
SET_TRACK_PAYLOAD(v)) // compound
#define SET_FALSE(v) \
(Assert_REBVAL_Writable((v), __FILE__, __LINE__), \
(v)->header.bits = REB_LOGIC | NOT_END_MASK \
| WRITABLE_MASK_DEBUG | VALUE_FLAG_FALSE, \
SET_TRACK_PAYLOAD(v)) // compound
#endif
#define SET_LOGIC(v,n) ((n) ? SET_TRUE(v) : SET_FALSE(v))
#define VAL_LOGIC(v) NOT(GET_VAL_FLAG((v), VALUE_FLAG_FALSE))
#ifdef NDEBUG
#define IS_CONDITIONAL_FALSE(v) \
GET_VAL_FLAG((v), VALUE_FLAG_FALSE)
#else
// In a debug build, we want to make sure that UNSET! is never asked
// about its conditional truth or falsehood; it's neither.
//
#define IS_CONDITIONAL_FALSE(v) \
IS_CONDITIONAL_FALSE_Debug(v)
#endif
#define IS_CONDITIONAL_TRUE(v) NOT(IS_CONDITIONAL_FALSE(v))
#define FALSE_VALUE (&PG_False_Value[0])
#define TRUE_VALUE (&PG_True_Value[0])
//
// Rebol Symbol
//
// !!! Historically Rebol used an unsigned 32-bit integer as a "symbol ID".
// These symbols did not participate in garbage collection and had to be
// looked up in a table to get their values. Ren-C is moving toward adapting
// REBSERs to be able to store words and canon words, as well as GC them.
// This starts moving the types to be the size of a platform pointer.
//
typedef REBUPT REBSYM;
/***********************************************************************
**
** DATATYPE - Datatype or pseudo-datatype
**
** !!! Consider rename to TYPE! once legacy TYPE? calls have been
** converted to TYPE-OF. Also consider a model where there are
** user types, and hence TYPE? may be able to return more than just
** one out of a set of 64 things.
**
***********************************************************************/
struct Reb_Datatype {
enum Reb_Kind kind;
REBARR *spec;
// REBINT min_type;
// REBINT max_type;
};
#define VAL_TYPE_KIND(v) ((v)->payload.datatype.kind)
#define VAL_TYPE_KIND_0(v) TO_0_FROM_KIND(VAL_TYPE_KIND(v))
#define VAL_TYPE_SPEC(v) ((v)->payload.datatype.spec)
// %words.r is arranged so that symbols for types are at the start
// Although REB_TRASH is 0, the 0 REBCNT used for symbol IDs is reserved
// for "no symbol". So there is no symbol for the "fake" type TRASH!
//
#define IS_KIND_SYM(s) ((s) < REB_MAX_0 + 1)
#define KIND_FROM_SYM(s) cast(enum Reb_Kind, KIND_FROM_0((s) - 1))
#define SYM_FROM_KIND(k) \
(assert((k) < REB_MAX), cast(REBSYM, TO_0_FROM_KIND(k) + 1))
#define VAL_TYPE_SYM(v) SYM_FROM_KIND((v)->payload.datatype.kind)
//#define VAL_MIN_TYPE(v) ((v)->payload.datatype.min_type)
//#define VAL_MAX_TYPE(v) ((v)->payload.datatype.max_type)
/***********************************************************************
**
** NUMBERS - Integer and other simple scalars
**
***********************************************************************/
#define VAL_INT32(v) (REBINT)((v)->payload.integer)
#define VAL_INT64(v) ((v)->payload.integer)
#define VAL_UNT64(v) ((v)->payload.unteger)
#define SET_INTEGER(v,n) \
(VAL_RESET_HEADER(v, REB_INTEGER), ((v)->payload.integer) = (n))
#define MAX_CHAR 0xffff
#define VAL_CHAR(v) ((v)->payload.character)
#define SET_CHAR(v,n) \
(VAL_RESET_HEADER((v), REB_CHAR), VAL_CHAR(v) = (n), NOOP)
#define IS_NUMBER(v) \
(VAL_TYPE(v) == REB_INTEGER || VAL_TYPE(v) == REB_DECIMAL)
/***********************************************************************
**
** DECIMAL -- Implementation-wise, a 'double'-precision floating
** point number in C (typically 64-bit).
**
***********************************************************************/
#define VAL_DECIMAL(v) ((v)->payload.decimal)
#define SET_DECIMAL(v,n) VAL_RESET_HEADER(v, REB_DECIMAL), VAL_DECIMAL(v) = (n)
/***********************************************************************
**
** MONEY -- Includes denomination and amount
**
** !!! The naming of "deci" used by MONEY! as "decimal" is a very
** bad overlap with DECIMAL! and also not very descriptive of what
** the properties of a "deci" are. Also, to be a useful money
** abstraction it should store the currency type, e.g. the three
** character ISO 4217 code (~15 bits to store)
**
** https://en.wikipedia.org/wiki/ISO_4217
**
***********************************************************************/
struct Reb_Money {
deci amount;
};
#define VAL_MONEY_AMOUNT(v) ((v)->payload.money.amount)
#define SET_MONEY_AMOUNT(v,n) \
(VAL_RESET_HEADER((v), REB_MONEY), VAL_MONEY_AMOUNT(v) = (n), NOOP)
/***********************************************************************
**
** DATE and TIME
**
***********************************************************************/
typedef struct reb_ymdz {
#ifdef ENDIAN_LITTLE
REBINT zone:7; // +/-15:00 res: 0:15
REBCNT day:5;
REBCNT month:4;
REBCNT year:16;
#else
REBCNT year:16;
REBCNT month:4;
REBCNT day:5;
REBINT zone:7; // +/-15:00 res: 0:15
#endif
} REBYMD;
typedef union reb_date {
REBYMD date;
REBCNT bits;
} REBDAT;
struct Reb_Time {
REBI64 time; // time in nanoseconds
REBDAT date;
};
#define VAL_TIME(v) ((v)->payload.time.time)
#define TIME_SEC(n) ((REBI64)(n) * 1000000000L)
#define MAX_SECONDS (((i64)1<<31)-1)
#define MAX_HOUR (MAX_SECONDS / 3600)
#define MAX_TIME ((REBI64)MAX_HOUR * HR_SEC)
#define NANO 1.0e-9
#define SEC_SEC ((REBI64)1000000000L)
#define MIN_SEC (60 * SEC_SEC)
#define HR_SEC (60 * 60 * SEC_SEC)
#define SEC_TIME(n) ((n) * SEC_SEC)
#define MIN_TIME(n) ((n) * MIN_SEC)
#define HOUR_TIME(n) ((n) * HR_SEC)
#define SECS_IN(n) ((n) / SEC_SEC)
#define VAL_SECS(n) (VAL_TIME(n) / SEC_SEC)
#define DEC_TO_SECS(n) (i64)(((n) + 5.0e-10) * SEC_SEC)
#define SECS_IN_DAY 86400
#define TIME_IN_DAY (SEC_TIME((i64)SECS_IN_DAY))
#define NO_TIME MIN_I64
#define MAX_YEAR 0x3fff
#define VAL_DATE(v) ((v)->payload.time.date)
#define VAL_YEAR(v) ((v)->payload.time.date.date.year)
#define VAL_MONTH(v) ((v)->payload.time.date.date.month)
#define VAL_DAY(v) ((v)->payload.time.date.date.day)
#define VAL_ZONE(v) ((v)->payload.time.date.date.zone)
#define ZONE_MINS 15
#define ZONE_SECS (ZONE_MINS*60)
#define MAX_ZONE (15 * (60/ZONE_MINS))
/***********************************************************************
**
** TUPLE
**
***********************************************************************/
typedef struct Reb_Tuple {
REBYTE tuple[12];
} REBTUP;
#define VAL_TUPLE(v) ((v)->payload.tuple.tuple + 1)
#define VAL_TUPLE_LEN(v) ((v)->payload.tuple.tuple[0])
#define MAX_TUPLE 10
/***********************************************************************
**
** PAIR