1 # Shared affine storage operations. Shape descriptors do not own another buffer. 2 import Base 3 import ./traversal_array.bend as Traversal 4 import ./proofs/storage_certificate.bend as Certificate 5 6 type Buffer<-Element: Data> is Type: 7 Buffer{storage: Array,length: Nat,certificate: Certificate.Certificate} 8 9 # Construction records evidence once, without visiting the owner or carrying 10 # certificates through the elementwise loop. 11 def allocate(~Element: Data,+depth: Nat,+value: Element,length: Nat) -> Buffer: 12 Buffer{Array.new(Element,depth,value),length,Certificate.allocated(Element,depth,value)} 13 14 def from_list(~Element: Data,+values: List<&2,Element>,+depth: Nat,+fallback: Element,length: Nat) -> Buffer: 15 Buffer{Traversal.write_list(~Element,values,0,Array.new(Element,depth,fallback)),length, 16 Certificate.write_list(~Element,values,0,Array.new(Element,depth,fallback),Certificate.allocated(Element,depth,fallback))} 17 18 # The values written from the offset on; the certificate follows the storage. 19 def write_at(~Element: Data,+values: List<&2,Element>,+offset: U32,buffer: Buffer) -> Buffer: 20 Buffer{storage,length,certificate} = buffer 21 Buffer{Traversal.write_list(~Element,values,offset,storage),length,Certificate.write_list(~Element,values,offset,storage,certificate)} 22 23 def capacity(~Element: Data,-storage: Array,certificate: Certificate.Certificate) -> U32: 24 Certificate.Certificate{depth,equation} = certificate 25 U32.shln(1,depth) 26 27 def bind_pair(~First: Type,~Second: Type,~Result: Type,pair: First & Second,body: First -> Second -> Result) -> Result: 28 (first,second) = pair 29 body(first,second) 30 31 # At most 32 word steps, independent of the allocation length. 32 def bit_length(remaining_bits: Nat,+value: U32) -> Nat: 33 match remaining_bits: 34 case 0n: 0n 35 case 1n+rest: 36 Bool.pick(Nat,U32.is_zero(value),0n,1n+bit_length(rest,U32.shr(value))) 37 38 # Positive requested lengths round to the next power-of-two capacity. 39 # The caller establishes the machine allocation limit before calling Array.new. 40 def allocation_depth(length: U32) -> Nat: 41 bit_length(32n,(length - 1 : U32)) 42 43 # Shared runtime depth calculation. The tail call stops at the significant 44 # word bits; allocation_depth remains the arithmetic model used by proofs. 45 def significant_bits(remaining: Nat,+value: U32,+bits: Nat,zero: Bool) -> Nat: 46 match remaining zero: 47 case 0n zero: bits 48 case 1n+rest True{}: bits 49 case 1n+rest False{}: 50 +next = U32.shr(value) 51 significant_bits(rest,next,1n+bits,U32.is_zero(next)) 52 53 def depth_for(+count: U32) -> Nat: 54 +value = (U32.max(count,1) - 1 : U32) 55 significant_bits(32n,value,0n,U32.is_zero(value)) 56 57 def write_list(~Element: Data,values: List<&2,Element>,+offset: U32,storage: Array) -> Array: 58 Traversal.write_list(~Element,values,offset,storage) 59 60 def read_list(~Element: Data,count: Nat,+offset: U32,storage: Array) -> List<&2,Element>: 61 match count: 62 case 0n: [] 63 case 1n+rest: 64 bind_pair(Array,Element,List<&2,Element>,Array.get(Element,storage,offset),storage => value => 65 value <> read_list(Element,rest,(offset + 1 : U32),storage)) 66 67 def read_when(~Element: Data,fallback: Element,valid: Bool,storage: Array,index: U32) -> Array & Element: 68 match valid: 69 case False{}: (storage,fallback) 70 case True{}: Array.get(Element,storage,index) 71 72 def read_sized(~Element: Data,fallback: Element,+index: U32,+logical_length: U32,sized: Array & U32) -> Array & Element: 73 (storage,capacity) = sized 74 read_when(Element,fallback,U32.is_lt(index,logical_length) && U32.is_le(logical_length,capacity),storage,index) 75 76 def read_or(~Element: Data,fallback: Element,storage: Array,index: U32,logical_length: U32) -> Array & Element: 77 read_sized(Element,fallback,index,logical_length,Array.size(Element,storage))