authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-05-05 15:25:17-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2019-05-05 15:25:17-04:00
log7a41af2632e693cd63476576bbbb965126e45b9b
tree5510ec982b29a84b35a649ee57a70ebf1949e7f5
parent5d347c01cf2942f7c04277242b440ceb695b3213
parent14a00d3825df34a993d7579869aa0204400e9fde
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #2422 from tgschultz/stdlib-packed-int-array

Added PackedIntArray, PackedIntSlice to std

3 files changed, 655 insertions(+), 0 deletions(-)

CMakeLists.txt+1
......@@ -629,6 +629,7 @@ set(ZIG_STD_FILES
629629 "os/windows/shell32.zig"
630630 "os/windows/util.zig"
631631 "os/zen.zig"
632 "packed_int_array.zig"
632633 "pdb.zig"
633634 "priority_queue.zig"
634635 "rand.zig"
std/packed_int_array.zig created+649
......@@ -0,0 +1,649 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const debug = std.debug;
4const testing = std.testing;
5
6pub fn PackedIntIo(comptime Int: type, comptime endian: builtin.Endian) type {
7 //The general technique employed here is to cast bytes in the array to a container
8 // integer (having bits % 8 == 0) large enough to contain the number of bits we want,
9 // then we can retrieve or store the new value with a relative minimum of masking
10 // and shifting. In this worst case, this means that we'll need an integer that's
11 // actually 1 byte larger than the minimum required to store the bits, because it
12 // is possible that the bits start at the end of the first byte, continue through
13 // zero or more, then end in the beginning of the last. But, if we try to access
14 // a value in the very last byte of memory with that integer size, that extra byte
15 // will be out of bounds. Depending on the circumstances of the memory, that might
16 // mean the OS fatally kills the program. Thus, we use a larger container (MaxIo)
17 // most of the time, but a smaller container (MinIo) when touching the last byte
18 // of the memory.
19 const int_bits = comptime std.meta.bitCount(Int);
20
21 //in the best case, this is the number of bytes we need to touch
22 // to read or write a value, as bits
23 const min_io_bits = ((int_bits + 7) / 8) * 8;
24
25 //in the worst case, this is the number of bytes we need to touch
26 // to read or write a value, as bits
27 const max_io_bits = switch (int_bits) {
28 0 => 0,
29 1 => 8,
30 2...9 => 16,
31 10...65535 => ((int_bits / 8) + 2) * 8,
32 else => unreachable,
33 };
34
35 //we bitcast the desired Int type to an unsigned version of itself
36 // to avoid issues with shifting signed ints.
37 const UnInt = @IntType(false, int_bits);
38
39 //The maximum container int type
40 const MinIo = @IntType(false, min_io_bits);
41
42 //The minimum container int type
43 const MaxIo = @IntType(false, max_io_bits);
44
45 return struct {
46 pub fn get(bytes: []const u8, index: usize, bit_offset: u7) Int {
47 if (int_bits == 0) return 0;
48
49 const bit_index = (index * int_bits) + bit_offset;
50 const max_end_byte = (bit_index + max_io_bits) / 8;
51
52 //Using the larger container size will potentially read out of bounds
53 if (max_end_byte > bytes.len) return getBits(bytes, MinIo, bit_index);
54 return getBits(bytes, MaxIo, bit_index);
55 }
56
57 fn getBits(bytes: []const u8, comptime Container: type, bit_index: usize) Int {
58 const container_bits = comptime std.meta.bitCount(Container);
59 const Shift = std.math.Log2Int(Container);
60
61 const start_byte = bit_index / 8;
62 const head_keep_bits = bit_index - (start_byte * 8);
63 const tail_keep_bits = container_bits - (int_bits + head_keep_bits);
64
65 //read bytes as container
66 const value_ptr = @ptrCast(*align(1) const Container, &bytes[start_byte]);
67 var value = value_ptr.*;
68
69 if (endian != builtin.endian) value = @bswap(Container, value);
70
71 switch (endian) {
72 .Big => {
73 value <<= @intCast(Shift, head_keep_bits);
74 value >>= @intCast(Shift, head_keep_bits);
75 value >>= @intCast(Shift, tail_keep_bits);
76 },
77 .Little => {
78 value <<= @intCast(Shift, tail_keep_bits);
79 value >>= @intCast(Shift, tail_keep_bits);
80 value >>= @intCast(Shift, head_keep_bits);
81 },
82 }
83
84 return @bitCast(Int, @truncate(UnInt, value));
85 }
86
87 pub fn set(bytes: []u8, index: usize, bit_offset: u3, int: Int) void {
88 if (int_bits == 0) return;
89
90 const bit_index = (index * int_bits) + bit_offset;
91 const max_end_byte = (bit_index + max_io_bits) / 8;
92
93 //Using the larger container size will potentially write out of bounds
94 if (max_end_byte > bytes.len) return setBits(bytes, MinIo, bit_index, int);
95 setBits(bytes, MaxIo, bit_index, int);
96 }
97
98 fn setBits(bytes: []u8, comptime Container: type, bit_index: usize, int: Int) void {
99 const container_bits = comptime std.meta.bitCount(Container);
100 const Shift = std.math.Log2Int(Container);
101
102 const start_byte = bit_index / 8;
103 const head_keep_bits = bit_index - (start_byte * 8);
104 const tail_keep_bits = container_bits - (int_bits + head_keep_bits);
105 const keep_shift = switch (endian) {
106 .Big => @intCast(Shift, tail_keep_bits),
107 .Little => @intCast(Shift, head_keep_bits),
108 };
109
110 //position the bits where they need to be in the container
111 const value = @intCast(Container, @bitCast(UnInt, int)) << keep_shift;
112
113 //read existing bytes
114 const target_ptr = @ptrCast(*align(1) Container, &bytes[start_byte]);
115 var target = target_ptr.*;
116
117 if (endian != builtin.endian) target = @bswap(Container, target);
118
119 //zero the bits we want to replace in the existing bytes
120 const inv_mask = @intCast(Container, std.math.maxInt(UnInt)) << keep_shift;
121 const mask = ~inv_mask;
122 target &= mask;
123
124 //merge the new value
125 target |= value;
126
127 if (endian != builtin.endian) target = @bswap(Container, target);
128
129 //save it back
130 target_ptr.* = target;
131 }
132
133 fn slice(bytes: []u8, bit_offset: u3, start: usize, end: usize) PackedIntSliceEndian(Int, endian) {
134 debug.assert(end >= start);
135
136 const length = end - start;
137 const bit_index = (start * int_bits) + bit_offset;
138 const start_byte = bit_index / 8;
139 const end_byte = (bit_index + (length * int_bits) + 7) / 8;
140 const new_bytes = bytes[start_byte..end_byte];
141
142 if (length == 0) return PackedIntSliceEndian(Int, endian).init(new_bytes[0..0], 0);
143
144 var new_slice = PackedIntSliceEndian(Int, endian).init(new_bytes, length);
145 new_slice.bit_offset = @intCast(u3, (bit_index - (start_byte * 8)));
146 return new_slice;
147 }
148
149 fn sliceCast(bytes: []u8, comptime NewInt: type, comptime new_endian: builtin.Endian, bit_offset: u3, old_len: usize) PackedIntSliceEndian(NewInt, new_endian) {
150 const new_int_bits = comptime std.meta.bitCount(NewInt);
151 const New = PackedIntSliceEndian(NewInt, new_endian);
152
153 const total_bits = (old_len * int_bits);
154 const new_int_count = total_bits / new_int_bits;
155
156 debug.assert(total_bits == new_int_count * new_int_bits);
157
158 var new = New.init(bytes, new_int_count);
159 new.bit_offset = bit_offset;
160
161 return new;
162 }
163 };
164}
165
166///Creates a bit-packed array of integers of type Int. Bits
167/// are packed using native endianess and without storing any meta
168/// data. PackedIntArray(i3, 8) will occupy exactly 3 bytes of memory.
169pub fn PackedIntArray(comptime Int: type, comptime int_count: usize) type {
170 return PackedIntArrayEndian(Int, builtin.endian, int_count);
171}
172
173///Creates a bit-packed array of integers of type Int. Bits
174/// are packed using specified endianess and without storing any meta
175/// data.
176pub fn PackedIntArrayEndian(comptime Int: type, comptime endian: builtin.Endian, comptime int_count: usize) type {
177 const int_bits = comptime std.meta.bitCount(Int);
178 const total_bits = int_bits * int_count;
179 const total_bytes = (total_bits + 7) / 8;
180
181 const Io = PackedIntIo(Int, endian);
182
183 return struct {
184 const Self = @This();
185
186 bytes: [total_bytes]u8,
187
188 ///Returns the number of elements in the packed array
189 pub fn len(self: Self) usize {
190 return int_count;
191 }
192
193 ///Initialize a packed array using an unpacked array
194 /// or, more likely, an array literal.
195 pub fn init(ints: [int_count]Int) Self {
196 var self = Self(undefined);
197 for (ints) |int, i| self.set(i, int);
198 return self;
199 }
200
201 ///Return the Int stored at index
202 pub fn get(self: Self, index: usize) Int {
203 debug.assert(index < int_count);
204 return Io.get(self.bytes, index, 0);
205 }
206
207 ///Copy int into the array at index
208 pub fn set(self: *Self, index: usize, int: Int) void {
209 debug.assert(index < int_count);
210 return Io.set(&self.bytes, index, 0, int);
211 }
212
213 ///Create a PackedIntSlice of the array from given start to given end
214 pub fn slice(self: *Self, start: usize, end: usize) PackedIntSliceEndian(Int, endian) {
215 debug.assert(start < int_count);
216 debug.assert(end <= int_count);
217 return Io.slice(&self.bytes, 0, start, end);
218 }
219
220 ///Create a PackedIntSlice of the array using NewInt as the bit width integer.
221 /// NewInt's bit width must fit evenly within the array's Int's total bits.
222 pub fn sliceCast(self: *Self, comptime NewInt: type) PackedIntSlice(NewInt) {
223 return self.sliceCastEndian(NewInt, endian);
224 }
225
226 ///Create a PackedIntSlice of the array using NewInt as the bit width integer
227 /// and new_endian as the new endianess. NewInt's bit width must fit evenly within
228 /// the array's Int's total bits.
229 pub fn sliceCastEndian(self: *Self, comptime NewInt: type, comptime new_endian: builtin.Endian) PackedIntSliceEndian(NewInt, new_endian) {
230 return Io.sliceCast(&self.bytes, NewInt, new_endian, 0, int_count);
231 }
232 };
233}
234
235///Uses a slice as a bit-packed block of int_count integers of type Int.
236/// Bits are packed using native endianess and without storing any meta
237/// data.
238pub fn PackedIntSlice(comptime Int: type) type {
239 return PackedIntSliceEndian(Int, builtin.endian);
240}
241
242///Uses a slice as a bit-packed block of int_count integers of type Int.
243/// Bits are packed using specified endianess and without storing any meta
244/// data.
245pub fn PackedIntSliceEndian(comptime Int: type, comptime endian: builtin.Endian) type {
246 const int_bits = comptime std.meta.bitCount(Int);
247 const Io = PackedIntIo(Int, endian);
248
249 return struct {
250 const Self = @This();
251
252 bytes: []u8,
253 int_count: usize,
254 bit_offset: u3,
255
256 ///Returns the number of elements in the packed slice
257 pub fn len(self: Self) usize {
258 return self.int_count;
259 }
260
261 ///Calculates the number of bytes required to store a desired count
262 /// of Ints
263 pub fn bytesRequired(int_count: usize) usize {
264 const total_bits = int_bits * int_count;
265 const total_bytes = (total_bits + 7) / 8;
266 return total_bytes;
267 }
268
269 ///Initialize a packed slice using the memory at bytes, with int_count
270 /// elements. bytes must be large enough to accomodate the requested
271 /// count.
272 pub fn init(bytes: []u8, int_count: usize) Self {
273 debug.assert(bytes.len >= bytesRequired(int_count));
274
275 return Self{
276 .bytes = bytes,
277 .int_count = int_count,
278 .bit_offset = 0,
279 };
280 }
281
282 ///Return the Int stored at index
283 pub fn get(self: Self, index: usize) Int {
284 debug.assert(index < self.int_count);
285 return Io.get(self.bytes, index, self.bit_offset);
286 }
287
288 ///Copy int into the array at index
289 pub fn set(self: *Self, index: usize, int: Int) void {
290 debug.assert(index < self.int_count);
291 return Io.set(self.bytes, index, self.bit_offset, int);
292 }
293
294 ///Create a PackedIntSlice of this slice from given start to given end
295 pub fn slice(self: Self, start: usize, end: usize) PackedIntSliceEndian(Int, endian) {
296 debug.assert(start < self.int_count);
297 debug.assert(end <= self.int_count);
298 return Io.slice(self.bytes, self.bit_offset, start, end);
299 }
300
301 ///Create a PackedIntSlice of this slice using NewInt as the bit width integer.
302 /// NewInt's bit width must fit evenly within this slice's Int's total bits.
303 pub fn sliceCast(self: Self, comptime NewInt: type) PackedIntSliceEndian(NewInt, endian) {
304 return self.sliceCastEndian(NewInt, endian);
305 }
306
307 ///Create a PackedIntSlice of this slice using NewInt as the bit width integer
308 /// and new_endian as the new endianess. NewInt's bit width must fit evenly within
309 /// this slice's Int's total bits.
310 pub fn sliceCastEndian(self: Self, comptime NewInt: type, comptime new_endian: builtin.Endian) PackedIntSliceEndian(NewInt, new_endian) {
311 return Io.sliceCast(self.bytes, NewInt, new_endian, self.bit_offset, self.int_count);
312 }
313 };
314}
315
316test "PackedIntArray" {
317 @setEvalBranchQuota(10000);
318 const max_bits = 256;
319 const int_count = 19;
320
321 comptime var bits = 0;
322 inline while (bits <= 256) : (bits += 1) {
323 //alternate unsigned and signed
324 const even = bits % 2 == 0;
325 const I = @IntType(even, bits);
326
327 const PackedArray = PackedIntArray(I, int_count);
328 const expected_bytes = ((bits * int_count) + 7) / 8;
329 testing.expect(@sizeOf(PackedArray) == expected_bytes);
330
331 var data = PackedArray(undefined);
332
333 //write values, counting up
334 var i = usize(0);
335 var count = I(0);
336 while (i < data.len()) : (i += 1) {
337 data.set(i, count);
338 if (bits > 0) count +%= 1;
339 }
340
341 //read and verify values
342 i = 0;
343 count = 0;
344 while (i < data.len()) : (i += 1) {
345 const val = data.get(i);
346 testing.expect(val == count);
347 if (bits > 0) count +%= 1;
348 }
349 }
350}
351
352test "PackedIntArray init" {
353 const PackedArray = PackedIntArray(u3, 8);
354 var packed_array = PackedArray.init([]u3{ 0, 1, 2, 3, 4, 5, 6, 7 });
355 var i = usize(0);
356 while (i < packed_array.len()) : (i += 1) testing.expect(packed_array.get(i) == i);
357}
358
359test "PackedIntSlice" {
360 @setEvalBranchQuota(10000);
361 const max_bits = 256;
362 const int_count = 19;
363 const total_bits = max_bits * int_count;
364 const total_bytes = (total_bits + 7) / 8;
365
366 var buffer: [total_bytes]u8 = undefined;
367
368 comptime var bits = 0;
369 inline while (bits <= 256) : (bits += 1) {
370 //alternate unsigned and signed
371 const even = bits % 2 == 0;
372 const I = @IntType(even, bits);
373 const P = PackedIntSlice(I);
374
375 var data = P.init(&buffer, int_count);
376
377 //write values, counting up
378 var i = usize(0);
379 var count = I(0);
380 while (i < data.len()) : (i += 1) {
381 data.set(i, count);
382 if (bits > 0) count +%= 1;
383 }
384
385 //read and verify values
386 i = 0;
387 count = 0;
388 while (i < data.len()) : (i += 1) {
389 const val = data.get(i);
390 testing.expect(val == count);
391 if (bits > 0) count +%= 1;
392 }
393 }
394}
395
396test "PackedIntSlice of PackedInt(Array/Slice)" {
397 const max_bits = 16;
398 const int_count = 19;
399
400 comptime var bits = 0;
401 inline while (bits <= max_bits) : (bits += 1) {
402 const Int = @IntType(false, bits);
403
404 const PackedArray = PackedIntArray(Int, int_count);
405 var packed_array = PackedArray(undefined);
406
407 const limit = (1 << bits);
408
409 var i = usize(0);
410 while (i < packed_array.len()) : (i += 1) {
411 packed_array.set(i, @intCast(Int, i % limit));
412 }
413
414 //slice of array
415 var packed_slice = packed_array.slice(2, 5);
416 testing.expect(packed_slice.len() == 3);
417 const ps_bit_count = (bits * packed_slice.len()) + packed_slice.bit_offset;
418 const ps_expected_bytes = (ps_bit_count + 7) / 8;
419 testing.expect(packed_slice.bytes.len == ps_expected_bytes);
420 testing.expect(packed_slice.get(0) == 2 % limit);
421 testing.expect(packed_slice.get(1) == 3 % limit);
422 testing.expect(packed_slice.get(2) == 4 % limit);
423 packed_slice.set(1, 7 % limit);
424 testing.expect(packed_slice.get(1) == 7 % limit);
425
426 //write through slice
427 testing.expect(packed_array.get(3) == 7 % limit);
428
429 //slice of a slice
430 const packed_slice_two = packed_slice.slice(0, 3);
431 testing.expect(packed_slice_two.len() == 3);
432 const ps2_bit_count = (bits * packed_slice_two.len()) + packed_slice_two.bit_offset;
433 const ps2_expected_bytes = (ps2_bit_count + 7) / 8;
434 testing.expect(packed_slice_two.bytes.len == ps2_expected_bytes);
435 testing.expect(packed_slice_two.get(1) == 7 % limit);
436 testing.expect(packed_slice_two.get(2) == 4 % limit);
437
438 //size one case
439 const packed_slice_three = packed_slice_two.slice(1, 2);
440 testing.expect(packed_slice_three.len() == 1);
441 const ps3_bit_count = (bits * packed_slice_three.len()) + packed_slice_three.bit_offset;
442 const ps3_expected_bytes = (ps3_bit_count + 7) / 8;
443 testing.expect(packed_slice_three.bytes.len == ps3_expected_bytes);
444 testing.expect(packed_slice_three.get(0) == 7 % limit);
445
446 //empty slice case
447 const packed_slice_empty = packed_slice.slice(0, 0);
448 testing.expect(packed_slice_empty.len() == 0);
449 testing.expect(packed_slice_empty.bytes.len == 0);
450
451 //slicing at byte boundaries
452 const packed_slice_edge = packed_array.slice(8, 16);
453 testing.expect(packed_slice_edge.len() == 8);
454 const pse_bit_count = (bits * packed_slice_edge.len()) + packed_slice_edge.bit_offset;
455 const pse_expected_bytes = (pse_bit_count + 7) / 8;
456 testing.expect(packed_slice_edge.bytes.len == pse_expected_bytes);
457 testing.expect(packed_slice_edge.bit_offset == 0);
458 }
459}
460
461test "PackedIntSlice accumulating bit offsets" {
462 //bit_offset is u3, so standard debugging asserts should catch
463 // anything
464 {
465 const PackedArray = PackedIntArray(u3, 16);
466 var packed_array = PackedArray(undefined);
467
468 var packed_slice = packed_array.slice(0, packed_array.len());
469 var i = usize(0);
470 while (i < packed_array.len() - 1) : (i += 1) {
471 packed_slice = packed_slice.slice(1, packed_slice.len());
472 }
473 }
474 {
475 const PackedArray = PackedIntArray(u11, 88);
476 var packed_array = PackedArray(undefined);
477
478 var packed_slice = packed_array.slice(0, packed_array.len());
479 var i = usize(0);
480 while (i < packed_array.len() - 1) : (i += 1) {
481 packed_slice = packed_slice.slice(1, packed_slice.len());
482 }
483 }
484}
485
486//@NOTE: As I do not have a big endian system to test this on,
487// big endian values were not tested
488test "PackedInt(Array/Slice) sliceCast" {
489 const PackedArray = PackedIntArray(u1, 16);
490 var packed_array = PackedArray.init([]u1{ 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1 });
491 const packed_slice_cast_2 = packed_array.sliceCast(u2);
492 const packed_slice_cast_4 = packed_slice_cast_2.sliceCast(u4);
493 var packed_slice_cast_9 = packed_array.slice(0, (packed_array.len() / 9) * 9).sliceCast(u9);
494 const packed_slice_cast_3 = packed_slice_cast_9.sliceCast(u3);
495
496 var i = usize(0);
497 while (i < packed_slice_cast_2.len()) : (i += 1) {
498 const val = switch (builtin.endian) {
499 .Big => 0b01,
500 .Little => 0b10,
501 };
502 testing.expect(packed_slice_cast_2.get(i) == val);
503 }
504 i = 0;
505 while (i < packed_slice_cast_4.len()) : (i += 1) {
506 const val = switch (builtin.endian) {
507 .Big => 0b0101,
508 .Little => 0b1010,
509 };
510 testing.expect(packed_slice_cast_4.get(i) == val);
511 }
512 i = 0;
513 while (i < packed_slice_cast_9.len()) : (i += 1) {
514 const val = 0b010101010;
515 testing.expect(packed_slice_cast_9.get(i) == val);
516 packed_slice_cast_9.set(i, 0b111000111);
517 }
518 i = 0;
519 while (i < packed_slice_cast_3.len()) : (i += 1) {
520 const val = switch (builtin.endian) {
521 .Big => if (i % 2 == 0) u3(0b111) else u3(0b000),
522 .Little => if (i % 2 == 0) u3(0b111) else u3(0b000),
523 };
524 testing.expect(packed_slice_cast_3.get(i) == val);
525 }
526}
527
528test "PackedInt(Array/Slice)Endian" {
529 {
530 const PackedArrayBe = PackedIntArrayEndian(u4, .Big, 8);
531 var packed_array_be = PackedArrayBe.init([]u4{
532 0,
533 1,
534 2,
535 3,
536 4,
537 5,
538 6,
539 7,
540 });
541 testing.expect(packed_array_be.bytes[0] == 0b00000001);
542 testing.expect(packed_array_be.bytes[1] == 0b00100011);
543
544 var i = usize(0);
545 while (i < packed_array_be.len()) : (i += 1) {
546 testing.expect(packed_array_be.get(i) == i);
547 }
548
549 var packed_slice_le = packed_array_be.sliceCastEndian(u4, .Little);
550 i = 0;
551 while (i < packed_slice_le.len()) : (i += 1) {
552 const val = if (i % 2 == 0) i + 1 else i - 1;
553 testing.expect(packed_slice_le.get(i) == val);
554 }
555
556 var packed_slice_le_shift = packed_array_be.slice(1, 5).sliceCastEndian(u4, .Little);
557 i = 0;
558 while (i < packed_slice_le_shift.len()) : (i += 1) {
559 const val = if (i % 2 == 0) i else i + 2;
560 testing.expect(packed_slice_le_shift.get(i) == val);
561 }
562 }
563
564 {
565 const PackedArrayBe = PackedIntArrayEndian(u11, .Big, 8);
566 var packed_array_be = PackedArrayBe.init([]u11{
567 0,
568 1,
569 2,
570 3,
571 4,
572 5,
573 6,
574 7,
575 });
576 testing.expect(packed_array_be.bytes[0] == 0b00000000);
577 testing.expect(packed_array_be.bytes[1] == 0b00000000);
578 testing.expect(packed_array_be.bytes[2] == 0b00000100);
579 testing.expect(packed_array_be.bytes[3] == 0b00000001);
580 testing.expect(packed_array_be.bytes[4] == 0b00000000);
581
582 var i = usize(0);
583 while (i < packed_array_be.len()) : (i += 1) {
584 testing.expect(packed_array_be.get(i) == i);
585 }
586
587 var packed_slice_le = packed_array_be.sliceCastEndian(u11, .Little);
588 testing.expect(packed_slice_le.get(0) == 0b00000000000);
589 testing.expect(packed_slice_le.get(1) == 0b00010000000);
590 testing.expect(packed_slice_le.get(2) == 0b00000000100);
591 testing.expect(packed_slice_le.get(3) == 0b00000000000);
592 testing.expect(packed_slice_le.get(4) == 0b00010000011);
593 testing.expect(packed_slice_le.get(5) == 0b00000000010);
594 testing.expect(packed_slice_le.get(6) == 0b10000010000);
595 testing.expect(packed_slice_le.get(7) == 0b00000111001);
596
597 var packed_slice_le_shift = packed_array_be.slice(1, 5).sliceCastEndian(u11, .Little);
598 testing.expect(packed_slice_le_shift.get(0) == 0b00010000000);
599 testing.expect(packed_slice_le_shift.get(1) == 0b00000000100);
600 testing.expect(packed_slice_le_shift.get(2) == 0b00000000000);
601 testing.expect(packed_slice_le_shift.get(3) == 0b00010000011);
602 }
603}
604
605//@NOTE: Need to manually update this list as more posix os's get
606// added to DirectAllocator. Windows can be added too when DirectAllocator
607// switches to VirtualAlloc.
608
609//These tests prove we aren't accidentally accessing memory past
610// the end of the array/slice by placing it at the end of a page
611// and reading the last element. The assumption is that the page
612// after this one is not mapped and will cause a segfault if we
613// don't account for the bounds.
614test "PackedIntArray at end of available memory" {
615 switch (builtin.os) {
616 .linux, .macosx, .ios, .freebsd, .netbsd => {},
617 else => return,
618 }
619 const PackedArray = PackedIntArray(u3, 8);
620
621 const Padded = struct {
622 _: [std.os.page_size - @sizeOf(PackedArray)]u8,
623 p: PackedArray,
624 };
625
626 var da = std.heap.DirectAllocator.init();
627 const allocator = &da.allocator;
628
629 var pad = try allocator.create(Padded);
630 defer allocator.destroy(pad);
631 pad.p.set(7, std.math.maxInt(u3));
632}
633
634test "PackedIntSlice at end of available memory" {
635 switch (builtin.os) {
636 .linux, .macosx, .ios, .freebsd, .netbsd => {},
637 else => return,
638 }
639 const PackedSlice = PackedIntSlice(u11);
640
641 var da = std.heap.DirectAllocator.init();
642 const allocator = &da.allocator;
643
644 var page = try allocator.alloc(u8, std.os.page_size);
645 defer allocator.free(page);
646
647 var p = PackedSlice.init(page[std.os.page_size - 2 ..], 1);
648 p.set(0, std.math.maxInt(u11));
649}
std/std.zig+5
......@@ -9,6 +9,10 @@ pub const DynLib = @import("dynamic_library.zig").DynLib;
99pub const HashMap = @import("hash_map.zig").HashMap;
1010pub const LinkedList = @import("linked_list.zig").LinkedList;
1111pub const Mutex = @import("mutex.zig").Mutex;
12pub const PackedIntArrayEndian = @import("packed_int_array.zig").PackedIntArrayEndian;
13pub const PackedIntArray = @import("packed_int_array.zig").PackedIntArray;
14pub const PackedIntSliceEndian = @import("packed_int_array.zig").PackedIntSliceEndian;
15pub const PackedIntSlice = @import("packed_int_array.zig").PackedIntSlice;
1216pub const PriorityQueue = @import("priority_queue.zig").PriorityQueue;
1317pub const StaticallyInitializedMutex = @import("statically_initialized_mutex.zig").StaticallyInitializedMutex;
1418pub const SegmentedList = @import("segmented_list.zig").SegmentedList;
......@@ -87,6 +91,7 @@ test "std" {
8791 _ = @import("net.zig");
8892 _ = @import("os.zig");
8993 _ = @import("pdb.zig");
94 _ = @import("packed_int_array.zig");
9095 _ = @import("priority_queue.zig");
9196 _ = @import("rand.zig");
9297 _ = @import("sort.zig");