Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Source file builderOps.ml
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848(******************************************************************************
* capnp-ocaml
*
* Copyright (c) 2013-2014, Paul Pelzl
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************)(* Builder operations. This provides most of the support code required for
the builder interface. *)typero=Message.rotyperw=Message.rwletinvalid_msg=Message.invalid_msgletsizeof_uint64=8moduleStructSizes=structtypet={data_words:int;pointer_words:int;}end(* ROM == "Read-Only Message"
RWM == "Read/Write Message"
Most of the builder operations are tied to the RWM types. The exceptional
cases are functions that make a copy from a source to a destination. *)moduleMake(ROM:MessageSig.S)(RWM:RPC.S)=structmoduleROM=RPC.None(ROM)moduleROC=CommonInc.Make[@inlined](ROM)moduleRWC=CommonInc.Make[@inlined](RWM)moduleRReader=ReaderInc.Make[@inlined](RWM)(* Given storage for a struct, get the pointer bytes for the given
struct-relative pointer index. *)letget_struct_pointer(struct_storage:('cap,_)RWM.StructStorage.t)(pointer_word:int):'capRWM.Slice.t=letpointers=struct_storage.RWM.StructStorage.pointersinletnum_pointers=pointers.RWM.Slice.len/sizeof_uint64in(* By design, this function should always be invoked after the struct
has been upgraded to at least the expected data and pointer
slice sizes. *)let()=assert(pointer_word<num_pointers)in{pointerswithRWM.Slice.start=pointers.RWM.Slice.start+(pointer_word*sizeof_uint64);RWM.Slice.len=sizeof_uint64;}(* Allocate storage for a struct within the specified message. *)letalloc_struct_storage(message:rwRWM.Message.t)~(data_words:int)~(pointer_words:int):(rw,'a)RWM.StructStorage.t=letstorage=RWM.Slice.allocmessage((data_words+pointer_words)*sizeof_uint64)inletdata={storagewithRWM.Slice.len=data_words*sizeof_uint64}inletpointers={storagewithRWM.Slice.start=data.RWM.Slice.start+data.RWM.Slice.len;RWM.Slice.len=pointer_words*sizeof_uint64;}inRWM.StructStorage.v~data~pointers(* Allocate storage for a list within the specified message. *)letalloc_list_storage(message:rwRWM.Message.t)(storage_type:ListStorageType.t)(num_elements:int):rwRWM.ListStorage.t=letstorage=matchstorage_typewith|ListStorageType.Empty->RWM.Slice.allocmessage0|ListStorageType.Bit->RWM.Slice.allocmessage(Util.ceil_rationum_elements8)|ListStorageType.Bytes1|ListStorageType.Bytes2|ListStorageType.Bytes4|ListStorageType.Bytes8|ListStorageType.Pointer->RWM.Slice.allocmessage(num_elements*(ListStorageType.get_byte_countstorage_type))|ListStorageType.Composite(data_words,pointer_words)->(* Composite list looks a little different from the other cases:
content is prefixed by a tag word which describes the shape of
the content. *)letword_count=1+(num_elements*(data_words+pointer_words))inletslice=RWM.Slice.allocmessage(word_count*sizeof_uint64)inlettag_descr={StructPointer.offset=num_elements;StructPointer.data_words=data_words;StructPointer.pointer_words=pointer_words;}inlettag_val=StructPointer.encodetag_descrinlet()=RWM.Slice.set_int64slice0tag_valinsliceinletopenRWM.ListStoragein{storage;storage_type;num_elements;}(* Initialize a far pointer so that it will point to the specified [content],
which is physically located in the given [content_slice].
[init_normal_pointer] describes how to construct a normal intra-segment
pointer which is appropriate for the content type; [init_far_pointer_tag]
provides a similar method for constructing the tag word found in a
"double far" landing pad. *)letinit_far_pointer(pointer_bytes:rwRWM.Slice.t)~(content:'a)~(content_slice:rwRWM.Slice.t)~(init_normal_pointer:rwRWM.Slice.t->'a->unit)~(init_far_pointer_tag:rwRWM.Slice.t->unit):unit=letlanding_pad_opt=RWM.Slice.alloc_in_segmentcontent_slice.RWM.Slice.msgcontent_slice.RWM.Slice.segment_idsizeof_uint64inbeginmatchlanding_pad_optwith|Somelanding_pad_bytes->(* Use a "normal" far pointer. *)let()=init_normal_pointerlanding_pad_bytescontentinletfar_pointer_desc={FarPointer.landing_pad=FarPointer.NormalPointer;FarPointer.offset=landing_pad_bytes.RWM.Slice.start/sizeof_uint64;FarPointer.segment_id=landing_pad_bytes.RWM.Slice.segment_id;}inletfar_pointer_val=FarPointer.encodefar_pointer_descinRWM.Slice.set_int64pointer_bytes0far_pointer_val|None->(* Use the "double far" convention. *)letlanding_pad_bytes=letlanding_pad=RWM.Slice.allocpointer_bytes.RWM.Slice.msg(2*sizeof_uint64)inletfar_pointer_desc={FarPointer.landing_pad=FarPointer.NormalPointer;FarPointer.offset=content_slice.RWM.Slice.start/sizeof_uint64;FarPointer.segment_id=content_slice.RWM.Slice.segment_id;}inlet()=RWM.Slice.set_int64landing_pad0(FarPointer.encodefar_pointer_desc)inlettag_slice={landing_padwithRWM.Slice.start=landing_pad.RWM.Slice.start+sizeof_uint64;RWM.Slice.len=sizeof_uint64;}inlet()=init_far_pointer_tagtag_sliceinlanding_padinletfar_pointer_desc={FarPointer.landing_pad=FarPointer.TaggedFarPointer;FarPointer.offset=landing_pad_bytes.RWM.Slice.start/sizeof_uint64;FarPointer.segment_id=landing_pad_bytes.RWM.Slice.segment_id;}inletfar_pointer_val=FarPointer.encodefar_pointer_descinRWM.Slice.set_int64pointer_bytes0far_pointer_valendletlist_pointer_type_of_storage_typetp=matchtpwith|ListStorageType.Empty->ListPointer.Void|ListStorageType.Bit->ListPointer.OneBitValue|ListStorageType.Bytes1->ListPointer.OneByteValue|ListStorageType.Bytes2->ListPointer.TwoByteValue|ListStorageType.Bytes4->ListPointer.FourByteValue|ListStorageType.Bytes8->ListPointer.EightByteValue|ListStorageType.Pointer->ListPointer.EightBytePointer|ListStorageType.Composite_->ListPointer.Composite(* Given a pointer location and list storage located within the same
message segment, modify the pointer so that it points to the list
storage. *)letinit_normal_list_pointer(pointer_bytes:rwRWM.Slice.t)(list_storage:rwRWM.ListStorage.t):unit=letstorage_slice=list_storage.RWM.ListStorage.storageinlet()=assert(storage_slice.RWM.Slice.segment_id=pointer_bytes.RWM.Slice.segment_id)inletoffset_bytes=storage_slice.RWM.Slice.start-RWM.Slice.get_endpointer_bytesinlet()=assert(offset_bytesland7=0)inletoffset_words=offset_bytes/8inletelement_type=list_pointer_type_of_storage_typelist_storage.RWM.ListStorage.storage_typeinletpointer_element_count=matchlist_storage.RWM.ListStorage.storage_typewith|ListStorageType.Composite(data_words,pointer_words)->list_storage.RWM.ListStorage.num_elements*(data_words+pointer_words)|_->list_storage.RWM.ListStorage.num_elementsinletpointer_descr={ListPointer.offset=offset_words;ListPointer.element_type=element_type;ListPointer.num_elements=pointer_element_count;}inletpointer_val=ListPointer.encodepointer_descrinRWM.Slice.set_int64pointer_bytes0pointer_val(* Initialize a list pointer so that it points to the specified list storage. *)letinit_list_pointer(pointer_bytes:rwRWM.Slice.t)(list_storage:rwRWM.ListStorage.t):unit=letstorage_slice=list_storage.RWM.ListStorage.storageinifstorage_slice.RWM.Slice.segment_id=pointer_bytes.RWM.Slice.segment_idthen(* Use a normal intra-segment list pointer. *)init_normal_list_pointerpointer_byteslist_storageelseletinit_far_pointer_tagtag_slice=letpointer_element_count=matchlist_storage.RWM.ListStorage.storage_typewith|ListStorageType.Composite(data_words,pointer_words)->list_storage.RWM.ListStorage.num_elements*(data_words+pointer_words)|_->list_storage.RWM.ListStorage.num_elementsinlettag_word_desc={ListPointer.offset=0;ListPointer.element_type=list_pointer_type_of_storage_typelist_storage.RWM.ListStorage.storage_type;ListPointer.num_elements=pointer_element_count;}inRWM.Slice.set_int64tag_slice0(ListPointer.encodetag_word_desc)ininit_far_pointerpointer_bytes~content:list_storage~content_slice:list_storage.RWM.ListStorage.storage~init_normal_pointer:init_normal_list_pointer~init_far_pointer_tag(* Given a pointer location and struct storage located within the same
message segment, modify the pointer so that it points to the struct
storage. *)letinit_normal_struct_pointer(pointer_bytes:rwRWM.Slice.t)(struct_storage:('cap,_)RWM.StructStorage.t):unit=let()=assert(struct_storage.RWM.StructStorage.data.RWM.Slice.segment_id=pointer_bytes.RWM.Slice.segment_id)inletpointer_descr={StructPointer.offset=(struct_storage.RWM.StructStorage.data.RWM.Slice.start-RWM.Slice.get_endpointer_bytes)/8;StructPointer.data_words=struct_storage.RWM.StructStorage.data.RWM.Slice.len/8;StructPointer.pointer_words=struct_storage.RWM.StructStorage.pointers.RWM.Slice.len/8;}inletpointer_val=StructPointer.encodepointer_descrinRWM.Slice.set_int64pointer_bytes0pointer_val(* Initialize a struct pointer so that it points to the specified
struct storage. *)letinit_struct_pointer(pointer_bytes:rwRWM.Slice.t)(struct_storage:('cap,_)RWM.StructStorage.t):unit=ifstruct_storage.RWM.StructStorage.data.RWM.Slice.segment_id=pointer_bytes.RWM.Slice.segment_idthen(* Use a normal intra-segment struct pointer. *)init_normal_struct_pointerpointer_bytesstruct_storageelseletinit_far_pointer_tagtag_slice=lettag_word_desc={StructPointer.offset=0;StructPointer.data_words=struct_storage.RWM.StructStorage.data.RWM.Slice.len/8;StructPointer.pointer_words=struct_storage.RWM.StructStorage.pointers.RWM.Slice.len/8;}inRWM.Slice.set_int64tag_slice0(StructPointer.encodetag_word_desc)inletcontent_slice={struct_storage.RWM.StructStorage.datawithRWM.Slice.len=struct_storage.RWM.StructStorage.data.RWM.Slice.len+struct_storage.RWM.StructStorage.pointers.RWM.Slice.len}ininit_far_pointerpointer_bytes~content:struct_storage~content_slice~init_normal_pointer:init_normal_struct_pointer~init_far_pointer_tag(* Copy a pointer from the source slice to the destination slice. This
copies the pointer only, not the pointed-to data. If the source
and destination are in different segments, this may result in
allocating additional message space to instantiate a far pointer. *)letshallow_copy_pointer~(src:'capRWM.Slice.t)~(dest:rwRWM.Slice.t):unit=matchRWC.deref_pointersrcwith|RWM.Object.None->RWM.Slice.set_int64dest0Int64.zero|RWM.Object.Listlist_storage->init_list_pointerdestlist_storage|RWM.Object.Structstruct_storage->init_struct_pointerdeststruct_storage|RWM.Object.Capability_->letword=RWM.Slice.get_int64src0inRWM.Slice.set_int64dest0word(* Copy a struct from the source slice to the destination slice. This
is a shallow copy; the data section is copied in bitwise fashion,
and the pointers are copied using [shallow_copy_pointer]. *)letshallow_copy_struct~(src:('cap,_)RWM.StructStorage.t)~(dest:(rw,_)RWM.StructStorage.t):unit=letopenRWM.StructStorageinletdata_copy_size=minsrc.data.RWM.Slice.lendest.data.RWM.Slice.leninlet()=RWM.Slice.blit~src:src.data~src_pos:0~dst:dest.data~dst_pos:0~len:data_copy_sizeinletpointer_copy_size=minsrc.pointers.RWM.Slice.lendest.pointers.RWM.Slice.leninletpointer_copy_words=pointer_copy_size/sizeof_uint64infori=0topointer_copy_words-1doletsrc_pointer=get_struct_pointersrciinletdest_pointer=get_struct_pointerdestiinshallow_copy_pointer~src:src_pointer~dest:dest_pointerdone(* Upgrade a List<Struct> so that each of the elements is at least as large
as the requirements of the current schema version. In general, this will
allocate a new list, make a shallow copy of the old data into the new list,
zero out the old data, and update the list pointer to reflect the change.
If the schema has not changed, this is a noop.
Returns the new list storage descriptor. *)letupgrade_struct_list(pointer_bytes:rwRWM.Slice.t)(list_storage:rwRWM.ListStorage.t)~(data_words:int)~(pointer_words:int):rwRWM.ListStorage.t=letneeds_upgrade=matchlist_storage.RWM.ListStorage.storage_typewith|ListStorageType.Empty->data_words*sizeof_uint64>0||pointer_words>0|ListStorageType.Bytes1|ListStorageType.Bytes2|ListStorageType.Bytes4|ListStorageType.Bytes8->letorig_data_size=ListStorageType.get_byte_countlist_storage.RWM.ListStorage.storage_typeindata_words*sizeof_uint64>orig_data_size||pointer_words>0|ListStorageType.Pointer->data_words>0||pointer_words>1|ListStorageType.Composite(orig_data_words,orig_pointer_words)->data_words>orig_data_words||pointer_words>orig_pointer_words|ListStorageType.Bit->invalid_msg"decoded List<Bool> where struct list was expected"inifneeds_upgradethenletmessage=pointer_bytes.RWM.Slice.msginletnew_storage=alloc_list_storagemessage(ListStorageType.Composite(data_words,pointer_words))list_storage.RWM.ListStorage.num_elementsinletsrc_struct_of_index=RWC.make_struct_of_list_indexlist_storageinletdest_struct_of_index=RWC.make_struct_of_list_indexnew_storageinfori=0tolist_storage.RWM.ListStorage.num_elements-1doshallow_copy_struct~src:(src_struct_of_indexi)~dest:(dest_struct_of_indexi)done;let()=init_list_pointerpointer_bytesnew_storageinletcontent_slice=list_storage.RWM.ListStorage.storageinlet()=RWM.Slice.zero_outcontent_slice~pos:0~len:content_slice.RWM.Slice.leninnew_storageelselist_storage(* Given a pointer which is expected to be a list pointer, compute the
corresponding list storage descriptor. If the pointer is null, storage
for a default list is immediately allocated using [alloc_default_list]. *)letderef_list_pointer?(struct_sizes:StructSizes.toption)~(create_default:rwRWM.Message.t->rwRWM.ListStorage.t)(pointer_bytes:rwRWM.Slice.t):rwRWM.ListStorage.t=matchRReader.deref_list_pointerpointer_byteswith|None->letlist_storage=create_defaultpointer_bytes.RWM.Slice.msginlet()=init_list_pointerpointer_byteslist_storageinlist_storage|Somelist_storage->beginmatchstruct_sizeswith|Some{StructSizes.data_words;StructSizes.pointer_words}->upgrade_struct_listpointer_byteslist_storage~data_words~pointer_words|None->list_storageend(* Set a struct to all-zeros. Pointers are not followed. *)letshallow_zero_out_struct(struct_storage:(rw,_)RWM.StructStorage.t):unit=letopenRWM.StructStorageinRWM.Slice.zero_outstruct_storage.data~pos:0~len:struct_storage.data.RWM.Slice.len;RWM.Slice.zero_outstruct_storage.pointers~pos:0~len:struct_storage.pointers.RWM.Slice.len(* Upgrade a struct so that its data and pointer regions are at least as large
as the protocol currently specifies. If the [orig] struct satisfies the
requirements of the [data_words] and [pointer_words], this is a no-op.
Otherwise a new struct is allocated, the data is copied over, the [orig]
is zeroed out, and the pointer to the struct is updated.
Returns: new struct descriptor (possibly the same as the old one). *)letupgrade_struct(pointer_bytes:rwRWM.Slice.t)(orig:(rw,_)RWM.StructStorage.t)~(data_words:int)~(pointer_words:int):(rw,_)RWM.StructStorage.t=letopenRWM.StructStorageiniforig.data.RWM.Slice.len<data_words*sizeof_uint64||orig.pointers.RWM.Slice.len<pointer_words*sizeof_uint64thenletnew_storage=alloc_struct_storageorig.data.RWM.Slice.msg~data_words~pointer_wordsinlet()=shallow_copy_struct~src:orig~dest:new_storageinlet()=init_struct_pointerpointer_bytesnew_storageinlet()=shallow_zero_out_structoriginnew_storageelseorig(* Given a pointer which is expected to be a struct pointer, compute the
corresponding struct storage descriptor. If the pointer is null, storage
for a default struct is immediately allocated using [alloc_default_struct].
[data_words] and [pointer_words] indicate the expected structure layout;
if the struct has a smaller layout (i.e. from an older protocol version),
then a new struct is allocated and the data is copied over. *)letderef_struct_pointer~(create_default:rwRWM.Message.t->(rw,'a)RWM.StructStorage.t)~(data_words:int)~(pointer_words:int)(pointer_bytes:rwRWM.Slice.t):(rw,'a)RWM.StructStorage.t=matchRReader.deref_struct_pointerpointer_byteswith|None->letstruct_storage=create_defaultpointer_bytes.RWM.Slice.msginlet()=init_struct_pointerpointer_bytesstruct_storageinstruct_storage|Somestruct_storage->upgrade_structpointer_bytesstruct_storage~data_words~pointer_words(* Given a [src] pointer to an arbitrary struct or list, first create a
deep copy of the pointed-to data then store a pointer to the data in
[dest]. *)letrecdeep_copy_pointer~(src:'capROM.Slice.t)~(dest:rwRWM.Slice.t):unit=matchROC.deref_pointersrcwith|ROM.Object.None->RWM.Slice.set_int64dest0Int64.zero|ROM.Object.Listsrc_list_storage->letdest_list_storage=deep_copy_list~src:src_list_storage~dest_message:dest.RWM.Slice.msg()ininit_list_pointerdestdest_list_storage|ROM.Object.Structsrc_struct_storage->letdest_struct_storage=letdata_words=src_struct_storage.ROM.StructStorage.data.ROM.Slice.len/sizeof_uint64inletpointer_words=src_struct_storage.ROM.StructStorage.pointers.ROM.Slice.len/sizeof_uint64indeep_copy_struct~src:src_struct_storage~dest_message:dest.RWM.Slice.msg~data_words~pointer_wordsininit_struct_pointerdestdest_struct_storage|ROM.Object.Capability_->letword=ROM.Slice.get_int64src0inRWM.Slice.set_int64dest0word(* Given a [src] struct storage descriptor, first allocate storage in
[dest_message] for a copy of the struct and then fill the allocated
region with a deep copy. [data_words] and [pointer_words] specify the
minimum allocation regions for the destination struct, and may exceed the
corresponding sizes from the [src] (for example, when fields are added
during a schema upgrade).
*)anddeep_copy_struct~(src:('cap,_)ROM.StructStorage.t)~(dest_message:rwRWM.Message.t)~(data_words:int)~(pointer_words:int):(rw,_)RWM.StructStorage.t=letsrc_data_words=src.ROM.StructStorage.data.ROM.Slice.len/sizeof_uint64inletsrc_pointer_words=src.ROM.StructStorage.pointers.ROM.Slice.len/sizeof_uint64inletdest_data_words=maxdata_wordssrc_data_wordsinletdest_pointer_words=maxpointer_wordssrc_pointer_wordsinletdest=alloc_struct_storagedest_message~data_words:dest_data_words~pointer_words:dest_pointer_wordsinlet()=deep_copy_struct_to_dest~src~destindest(* As [deep_copy_struct], but the destination is already allocated. *)anddeep_copy_struct_to_dest~(src:('cap,_)ROM.StructStorage.t)~(dest:(rw,_)RWM.StructStorage.t):unit=letdata_bytes=minsrc.ROM.StructStorage.data.ROM.Slice.lendest.RWM.StructStorage.data.RWM.Slice.leninlet()=assert((data_bytesmodsizeof_uint64)=0)inletdata_words=data_bytes/sizeof_uint64inlet()=letsrc_data=src.ROM.StructStorage.datainletdest_data=dest.RWM.StructStorage.datainfori=0todata_words-1doletbyte_ofs=i*sizeof_uint64inletword=ROM.Slice.get_int64src_databyte_ofsinRWM.Slice.set_int64dest_databyte_ofsworddoneinletsrc_pointer_words=src.ROM.StructStorage.pointers.ROM.Slice.len/sizeof_uint64inletdest_pointer_words=dest.RWM.StructStorage.pointers.RWM.Slice.len/sizeof_uint64inletpointer_words=minsrc_pointer_wordsdest_pointer_wordsinfori=0topointer_words-1doletsrc_pointer=letopenROM.StructStoragein{src.pointerswithROM.Slice.start=src.pointers.ROM.Slice.start+(i*sizeof_uint64);ROM.Slice.len=sizeof_uint64;}inletdest_pointer=letopenRWM.StructStoragein{dest.pointerswithRWM.Slice.start=dest.pointers.RWM.Slice.start+(i*sizeof_uint64);RWM.Slice.len=sizeof_uint64;}indeep_copy_pointer~src:src_pointer~dest:dest_pointerdone(* Given a [src] list storage descriptor, first allocate storage in
[dest_message] for a copy of the list and then fill the allocated
region with deep copies of the list elements. If the [struct_sizes]
are provided, the deep copy will create inlined structs which have
data and pointer regions at least as large as specified. *)anddeep_copy_list?(struct_sizes:StructSizes.toption)~(src:'capROM.ListStorage.t)~(dest_message:rwRWM.Message.t)():rwRWM.ListStorage.t=matchstruct_sizeswith|Some{StructSizes.data_words;StructSizes.pointer_words}->deep_copy_struct_list~src~dest_message~data_words~pointer_words|None->letdest=alloc_list_storagedest_messagesrc.ROM.ListStorage.storage_typesrc.ROM.ListStorage.num_elementsinletcopy_by_valueword_count=fori=0toword_count-1doletbyte_ofs=i*sizeof_uint64inletword=ROM.Slice.get_int64src.ROM.ListStorage.storagebyte_ofsinRWM.Slice.set_int64dest.RWM.ListStorage.storagebyte_ofsworddoneinlet()=matchsrc.ROM.ListStorage.storage_typewith|ListStorageType.Empty->()|ListStorageType.Bit->lettotal_bytes=Util.ceil_ratiosrc.ROM.ListStorage.num_elements8inlettotal_words=Util.ceil_ratiototal_bytessizeof_uint64incopy_by_valuetotal_words|ListStorageType.Bytes1|ListStorageType.Bytes2|ListStorageType.Bytes4|ListStorageType.Bytes8->letbyte_count=ListStorageType.get_byte_countsrc.ROM.ListStorage.storage_typeinlettotal_bytes=src.ROM.ListStorage.num_elements*byte_countinlettotal_words=Util.ceil_ratiototal_bytessizeof_uint64incopy_by_valuetotal_words|ListStorageType.Pointer->fori=0tosrc.ROM.ListStorage.num_elements-1doletsrc_pointer=letopenROM.ListStoragein{src.storagewithROM.Slice.start=src.storage.ROM.Slice.start+(i*sizeof_uint64);ROM.Slice.len=sizeof_uint64;}inletdest_pointer=letopenRWM.ListStoragein{dest.storagewithRWM.Slice.start=dest.storage.RWM.Slice.start+(i*sizeof_uint64);RWM.Slice.len=sizeof_uint64;}indeep_copy_pointer~src:src_pointer~dest:dest_pointerdone|ListStorageType.Composite(data_words,pointer_words)->letwords_per_element=data_words+pointer_wordsin(* Account for the composite tag word. We don't need to copy the
tag, because a new one was created by [alloc_list_storage]. *)letsrc_content_offset=src.ROM.ListStorage.storage.ROM.Slice.start+sizeof_uint64inletdest_content_offset=dest.RWM.ListStorage.storage.RWM.Slice.start+sizeof_uint64infori=0tosrc.ROM.ListStorage.num_elements-1doletsrc_struct=letopenROM.ListStorageinROM.StructStorage.v~data:{src.storagewithROM.Slice.start=src_content_offset+(i*words_per_element*sizeof_uint64);ROM.Slice.len=data_words*sizeof_uint64;}~pointers:{src.storagewithROM.Slice.start=src_content_offset+((i*words_per_element)+data_words)*sizeof_uint64;ROM.Slice.len=pointer_words*sizeof_uint64;}inletdest_struct=letopenRWM.ListStorageinRWM.StructStorage.v~data:{dest.storagewithRWM.Slice.start=dest_content_offset+(i*words_per_element*sizeof_uint64);RWM.Slice.len=data_words*sizeof_uint64;}~pointers:{dest.storagewithRWM.Slice.start=dest_content_offset+((i*words_per_element)+data_words)*sizeof_uint64;RWM.Slice.len=pointer_words*sizeof_uint64;}indeep_copy_struct_to_dest~src:src_struct~dest:dest_structdoneindest(* Given a List<Struct>, allocate new (orphaned) list storage and
deep-copy the list elements into it. The newly-allocated list
shall have data and pointers regions sized according to
[data_words] and [pointer_words], to support schema upgrades;
if the source has a larger data/pointers region, the additional
bytes are copied as well.
Returns: new list storage
*)anddeep_copy_struct_list~(src:'capROM.ListStorage.t)~(dest_message:rwRWM.Message.t)~(data_words:int)~(pointer_words:int):rwRWM.ListStorage.t=letdest_storage=let(dest_data_words,dest_pointer_words)=matchsrc.ROM.ListStorage.storage_typewith|ListStorageType.Empty|ListStorageType.Bytes1|ListStorageType.Bytes2|ListStorageType.Bytes4|ListStorageType.Bytes8|ListStorageType.Pointer->(data_words,pointer_words)|ListStorageType.Composite(src_data_words,src_pointer_words)->(maxdata_wordssrc_data_words,maxpointer_wordssrc_pointer_words)|ListStorageType.Bit->invalid_msg"decoded unexpected list type where List<composite> was expected"inalloc_list_storagedest_message(ListStorageType.Composite(dest_data_words,dest_pointer_words))src.ROM.ListStorage.num_elementsinletsrc_struct_of_list_index=ROC.make_struct_of_list_indexsrcinletdest_struct_of_list_index=RWC.make_struct_of_list_indexdest_storageinfori=0tosrc.ROM.ListStorage.num_elements-1doletsrc_struct=src_struct_of_list_indexiinletdest_struct=dest_struct_of_list_indexiindeep_copy_struct_to_dest~src:src_struct~dest:dest_structdone;dest_storage(* Recursively zero out all data which this pointer points to. The pointer
value is unchanged. *)letrecdeep_zero_pointer(pointer_bytes:rwRWM.Slice.t):unit=matchRWC.deref_pointerpointer_byteswith|RWM.Object.None->()|RWM.Object.Listlist_storage->deep_zero_listlist_storage|RWM.Object.Structstruct_storage->deep_zero_structstruct_storage|RWM.Object.Capabilityindex->letattachments=RWM.Message.get_attachmentspointer_bytes.RWM.Slice.msginRWM.Untyped.clear_capattachmentsindexanddeep_zero_list(list_storage:rwRWM.ListStorage.t):unit=matchlist_storage.RWM.ListStorage.storage_typewith|ListStorageType.Empty|ListStorageType.Bit|ListStorageType.Bytes1|ListStorageType.Bytes2|ListStorageType.Bytes4|ListStorageType.Bytes8->RWM.Slice.zero_outlist_storage.RWM.ListStorage.storage~pos:0~len:list_storage.RWM.ListStorage.storage.RWM.Slice.len|ListStorageType.Pointer->letopenRWM.ListStorageinlet()=fori=0tolist_storage.num_elements-1doletpointer_bytes={list_storage.storagewithRWM.Slice.start=list_storage.storage.RWM.Slice.start+(i*sizeof_uint64);RWM.Slice.len=sizeof_uint64;}indeep_zero_pointerpointer_bytesdoneinRWM.Slice.zero_outlist_storage.storage~pos:0~len:list_storage.storage.RWM.Slice.len|ListStorageType.Composite(data_words,pointer_words)->letopenRWM.ListStorageinlet()=lettotal_words=data_words+pointer_wordsinfori=0tolist_storage.num_elements-1do(* Note: delegating to [deep_zero_struct] is kind of inefficient
because it means we clear most of the list twice. *)letdata={list_storage.storagewithRWM.Slice.start=list_storage.storage.RWM.Slice.start+(i*total_words*sizeof_uint64);RWM.Slice.len=data_words*sizeof_uint64;}inletpointers={list_storage.storagewithRWM.Slice.start=RWM.Slice.get_enddata;RWM.Slice.len=pointer_words*sizeof_uint64;}indeep_zero_struct(RWM.StructStorage.v~data~pointers)donein(* Composite lists prefix the data with a tag word, so clean up
the tag word along with everything else *)letcontent_slice={list_storage.storagewithRWM.Slice.start=list_storage.storage.RWM.Slice.start-sizeof_uint64;RWM.Slice.len=list_storage.storage.RWM.Slice.len+sizeof_uint64;}inRWM.Slice.zero_outcontent_slice~pos:0~len:content_slice.RWM.Slice.lenanddeep_zero_struct(struct_storage:(rw,_)RWM.StructStorage.t):unit=letopenRWM.StructStorageinletpointer_words=struct_storage.pointers.RWM.Slice.len/sizeof_uint64infori=0topointer_words-1doletpointer_bytes=get_struct_pointerstruct_storageiindeep_zero_pointerpointer_bytesdone;RWM.Slice.zero_outstruct_storage.data~pos:0~len:struct_storage.data.RWM.Slice.len;RWM.Slice.zero_outstruct_storage.pointers~pos:0~len:struct_storage.pointers.RWM.Slice.lenend