@@ -507,15 +507,223 @@ pub fn dupeZ(allocator: Allocator, comptime T: type, m: []const T) ![:0]T {
507507 return new_buf [0.. m .len :0 ];
508508}
509509
510+ /// This function allows a runtime `alignment` value. Callers should generally prefer
511+ /// to call the `alloc*` functions.
512+ pub fn allocBytes (
513+ self : Allocator ,
514+ /// Must be >= 1.
515+ /// Must be a power of 2.
516+ /// Returned slice's pointer will have this alignment.
517+ alignment : u29 ,
518+ byte_count : usize ,
519+ /// 0 indicates the length of the slice returned MUST match `byte_count` exactly
520+ /// non-zero means the length of the returned slice must be aligned by `len_align`
521+ /// `byte_count` must be aligned by `len_align`
522+ len_align : u29 ,
523+ return_address : usize ,
524+ ) Error ! []u8 {
525+ const new_mem = try self .rawAlloc (byte_count , alignment , len_align , return_address );
526+ // TODO: https://github.com/ziglang/zig/issues/4298
527+ @memset (new_mem .ptr , undefined , new_mem .len );
528+ return new_mem ;
529+ }
530+
531+ test "allocBytes" {
532+ const number_of_bytes : usize = 10 ;
533+ var runtime_alignment : u29 = 2 ;
534+
535+ {
536+ const new_mem = try std .testing .allocator .allocBytes (runtime_alignment , number_of_bytes , 0 , @returnAddress ());
537+ defer std .testing .allocator .free (new_mem );
538+
539+ try std .testing .expectEqual (number_of_bytes , new_mem .len );
540+ try std .testing .expect (mem .isAligned (@ptrToInt (new_mem .ptr ), runtime_alignment ));
541+ }
542+
543+ runtime_alignment = 8 ;
544+
545+ {
546+ const new_mem = try std .testing .allocator .allocBytes (runtime_alignment , number_of_bytes , 0 , @returnAddress ());
547+ defer std .testing .allocator .free (new_mem );
548+
549+ try std .testing .expectEqual (number_of_bytes , new_mem .len );
550+ try std .testing .expect (mem .isAligned (@ptrToInt (new_mem .ptr ), runtime_alignment ));
551+ }
552+ }
553+
554+ test "allocBytes non-zero len_align" {
555+ const number_of_bytes : usize = 10 ;
556+ var runtime_alignment : u29 = 1 ;
557+ var len_align : u29 = 2 ;
558+
559+ {
560+ const new_mem = try std .testing .allocator .allocBytes (runtime_alignment , number_of_bytes , len_align , @returnAddress ());
561+ defer std .testing .allocator .free (new_mem );
562+
563+ try std .testing .expect (new_mem .len >= number_of_bytes );
564+ try std .testing .expect (new_mem .len % len_align == 0 );
565+ try std .testing .expect (mem .isAligned (@ptrToInt (new_mem .ptr ), runtime_alignment ));
566+ }
567+
568+ runtime_alignment = 16 ;
569+ len_align = 5 ;
570+
571+ {
572+ const new_mem = try std .testing .allocator .allocBytes (runtime_alignment , number_of_bytes , len_align , @returnAddress ());
573+ defer std .testing .allocator .free (new_mem );
574+
575+ try std .testing .expect (new_mem .len >= number_of_bytes );
576+ try std .testing .expect (new_mem .len % len_align == 0 );
577+ try std .testing .expect (mem .isAligned (@ptrToInt (new_mem .ptr ), runtime_alignment ));
578+ }
579+ }
580+
581+ /// Realloc is used to modify the size or alignment of an existing allocation,
582+ /// as well as to provide the allocator with an opportunity to move an allocation
583+ /// to a better location.
584+ /// The returned slice will have its pointer aligned at least to `new_alignment` bytes.
585+ ///
586+ /// This function allows a runtime `alignment` value. Callers should generally prefer
587+ /// to call the `realloc*` functions.
588+ ///
589+ /// If the size/alignment is greater than the previous allocation, and the requested new
590+ /// allocation could not be granted this function returns `error.OutOfMemory`.
591+ /// When the size/alignment is less than or equal to the previous allocation,
592+ /// this function returns `error.OutOfMemory` when the allocator decides the client
593+ /// would be better off keeping the extra alignment/size.
594+ /// Clients will call `resizeFn` when they require the allocator to track a new alignment/size,
595+ /// and so this function should only return success when the allocator considers
596+ /// the reallocation desirable from the allocator's perspective.
597+ ///
598+ /// As an example, `std.ArrayList` tracks a "capacity", and therefore can handle
599+ /// reallocation failure, even when `new_n` <= `old_mem.len`. A `FixedBufferAllocator`
600+ /// would always return `error.OutOfMemory` for `reallocFn` when the size/alignment
601+ /// is less than or equal to the old allocation, because it cannot reclaim the memory,
602+ /// and thus the `std.ArrayList` would be better off retaining its capacity.
603+ pub fn reallocBytes (
604+ self : Allocator ,
605+ /// Must be the same as what was returned from most recent call to `allocFn` or `resizeFn`.
606+ /// If `old_mem.len == 0` then this is a new allocation and `new_byte_count` must be >= 1.
607+ old_mem : []u8 ,
608+ /// If `old_mem.len == 0` then this is `undefined`, otherwise:
609+ /// Must be the same as what was passed to `allocFn`.
610+ /// Must be >= 1.
611+ /// Must be a power of 2.
612+ old_alignment : u29 ,
613+ /// If `new_byte_count` is 0 then this is a free and it is required that `old_mem.len != 0`.
614+ new_byte_count : usize ,
615+ /// Must be >= 1.
616+ /// Must be a power of 2.
617+ /// Returned slice's pointer will have this alignment.
618+ new_alignment : u29 ,
619+ /// 0 indicates the length of the slice returned MUST match `new_byte_count` exactly
620+ /// non-zero means the length of the returned slice must be aligned by `len_align`
621+ /// `new_byte_count` must be aligned by `len_align`
622+ len_align : u29 ,
623+ return_address : usize ,
624+ ) Error ! []u8 {
625+ if (old_mem .len == 0 ) {
626+ return self .allocBytes (new_alignment , new_byte_count , len_align , return_address );
627+ }
628+ if (new_byte_count == 0 ) {
629+ // TODO https://github.com/ziglang/zig/issues/4298
630+ @memset (old_mem .ptr , undefined , old_mem .len );
631+ self .rawFree (old_mem , old_alignment , return_address );
632+ return &[0 ]u8 {};
633+ }
634+
635+ if (mem .isAligned (@ptrToInt (old_mem .ptr ), new_alignment )) {
636+ if (new_byte_count <= old_mem .len ) {
637+ const shrunk_len = self .shrinkBytes (old_mem , old_alignment , new_byte_count , len_align , return_address );
638+ return old_mem .ptr [0.. shrunk_len ];
639+ }
640+
641+ if (self .rawResize (old_mem , old_alignment , new_byte_count , len_align , return_address )) | resized_len | {
642+ assert (resized_len >= new_byte_count );
643+ // TODO: https://github.com/ziglang/zig/issues/4298
644+ @memset (old_mem .ptr + new_byte_count , undefined , resized_len - new_byte_count );
645+ return old_mem .ptr [0.. resized_len ];
646+ }
647+ }
648+
649+ if (new_byte_count <= old_mem .len and new_alignment <= old_alignment ) {
650+ return error .OutOfMemory ;
651+ }
652+
653+ const new_mem = try self .rawAlloc (new_byte_count , new_alignment , len_align , return_address );
654+ @memcpy (new_mem .ptr , old_mem .ptr , math .min (new_byte_count , old_mem .len ));
655+
656+ // TODO https://github.com/ziglang/zig/issues/4298
657+ @memset (old_mem .ptr , undefined , old_mem .len );
658+ self .rawFree (old_mem , old_alignment , return_address );
659+
660+ return new_mem ;
661+ }
662+
663+ test "reallocBytes" {
664+ var new_mem : []u8 = &.{};
665+
666+ var new_byte_count : usize = 16 ;
667+ var runtime_alignment : u29 = 4 ;
668+
669+ // `new_mem.len == 0`, this is a new allocation
670+ {
671+ new_mem = try std .testing .allocator .reallocBytes (new_mem , undefined , new_byte_count , runtime_alignment , 0 , @returnAddress ());
672+ try std .testing .expectEqual (new_byte_count , new_mem .len );
673+ try std .testing .expect (mem .isAligned (@ptrToInt (new_mem .ptr ), runtime_alignment ));
674+ }
675+
676+ // `new_byte_count < new_mem.len`, this is a shrink, alignment is unmodified
677+ new_byte_count = 14 ;
678+ {
679+ new_mem = try std .testing .allocator .reallocBytes (new_mem , runtime_alignment , new_byte_count , runtime_alignment , 0 , @returnAddress ());
680+ try std .testing .expectEqual (new_byte_count , new_mem .len );
681+ try std .testing .expect (mem .isAligned (@ptrToInt (new_mem .ptr ), runtime_alignment ));
682+ }
683+
684+ // `new_byte_count < new_mem.len`, this is a shrink, alignment is decreased from 4 to 2
685+ runtime_alignment = 2 ;
686+ new_byte_count = 12 ;
687+ {
688+ new_mem = try std .testing .allocator .reallocBytes (new_mem , 4 , new_byte_count , runtime_alignment , 0 , @returnAddress ());
689+ try std .testing .expectEqual (new_byte_count , new_mem .len );
690+ try std .testing .expect (mem .isAligned (@ptrToInt (new_mem .ptr ), runtime_alignment ));
691+ }
692+
693+ // `new_byte_count > new_mem.len`, this is a growth, alignment is increased from 2 to 8
694+ runtime_alignment = 8 ;
695+ new_byte_count = 32 ;
696+ {
697+ new_mem = try std .testing .allocator .reallocBytes (new_mem , 2 , new_byte_count , runtime_alignment , 0 , @returnAddress ());
698+ try std .testing .expectEqual (new_byte_count , new_mem .len );
699+ try std .testing .expect (mem .isAligned (@ptrToInt (new_mem .ptr ), runtime_alignment ));
700+ }
701+
702+ // `new_byte_count == 0`, this is a free
703+ new_byte_count = 0 ;
704+ {
705+ new_mem = try std .testing .allocator .reallocBytes (new_mem , runtime_alignment , new_byte_count , runtime_alignment , 0 , @returnAddress ());
706+ try std .testing .expectEqual (new_byte_count , new_mem .len );
707+ }
708+ }
709+
510710/// Call `vtable.resize`, but caller guarantees that `new_len` <= `buf.len` meaning
511711/// than a `null` return value should be impossible.
512712/// This function allows a runtime `buf_align` value. Callers should generally prefer
513- /// to call `shrink` directly .
713+ /// to call `shrink`.
514714pub fn shrinkBytes (
515715 self : Allocator ,
716+ /// Must be the same as what was returned from most recent call to `allocFn` or `resizeFn`.
516717 buf : []u8 ,
718+ /// Must be the same as what was passed to `allocFn`.
719+ /// Must be >= 1.
720+ /// Must be a power of 2.
517721 buf_align : u29 ,
722+ /// Must be >= 1.
518723 new_len : usize ,
724+ /// 0 indicates the length of the slice returned MUST match `new_len` exactly
725+ /// non-zero means the length of the returned slice must be aligned by `len_align`
726+ /// `new_len` must be aligned by `len_align`
519727 len_align : u29 ,
520728 return_address : usize ,
521729) usize {
0 commit comments