123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170open!Coreletiobuf_destinationbuf=(* We give out an [Iobuf] with a shared underlying [Bigstring] but different pointers
so that when this is closed the provided buffer keeps its window, and we can test
the [Buffer_until_initialized] feature to ignore writes after close.
This also ensures our logic works when the window of [buf] is narrower than the
limits because [sub_shared] leads to a buffer with equal window and limits. *)letprovided_buf=Iobuf.sub_sharedbufinletmoduleDest=struct(* [next_buf] can be called multiple times even without running out of room, for
example via [Writer.Expert.force_switch_buffers]. But we can just keep giving back
the same buffer as long as it has room *)letnext_buf~ensure_capacity=ifensure_capacity>Iobuf.lengthprovided_bufthenfailwith"No more room in [iobuf_destination]";provided_buf;;letclose()=Iobuf.flip_loprovided_buf;Iobuf.resize~len:(Iobuf.lengthprovided_buf)buf;Iobuf.resize~len:0provided_buf;;endin(moduleDest:Writer_intf.Destination);;letraw_iobuf_destinationbuf=letmoduleDest=struct(* [next_buf] can be called multiple times even without running out of room, for
example via [Writer.Expert.force_switch_buffers]. But we can just keep giving back
the same buffer as long as it has room *)letnext_buf~ensure_capacity=ifensure_capacity>Iobuf.lengthbufthenfailwith"No more room in [iobuf_destination]";buf;;letclose()=()endin(moduleDest:Writer_intf.Destination);;letblack_hole_destination~len~touch_memory=letbuf=Iobuf.create~leniniftouch_memorythenIobuf.zerobuf;letmoduleDest=structletnext_buf~ensure_capacity=Iobuf.resetbuf;ifensure_capacity>Iobuf.lengthbufthenfailwith"Record too large for [black_hole_destination]";buf;;letclose()=()endin(moduleDest:Writer_intf.Destination);;(* A [Destination] which keeps buffers it gives out in a list and is able to write the
contents of those buffers to another [Destination]. *)moduleTemp_buffer:sigtypet={copy_to:(moduleWriter_intf.Destination)->unit;dest:(moduleWriter_intf.Destination)}valcreate:unit->tend=structtypet={copy_to:(moduleWriter_intf.Destination)->unit;dest:(moduleWriter_intf.Destination)}typeinternal={mutablebuffers:(read_write,Iobuf.seek)Iobuf.tlist}letcreate()=lett={buffers=[]}inletmoduleDest=structletnext_buf~ensure_capacity=letcapacity=Int.maxensure_capacity1_000inletbuf=Iobuf.create~len:capacityint.buffers<-buf::t.buffers;buf;;(* We have nowhere to flush to *)letclose()=()endinletdest=(moduleDest:Writer_intf.Destination)inletcopy_to(moduleD:Writer_intf.Destination)=letin_order_buffers=List.revt.buffersinletout_buf=ref(D.next_buf~ensure_capacity:0)inList.iterin_order_buffers~f:(funin_buf->Iobuf.flip_loin_buf;letin_buf_len=Iobuf.lengthin_bufinifIobuf.length!out_buf<in_buf_lenthenout_buf:=D.next_buf~ensure_capacity:in_buf_len;Iobuf.Blit_fill.blito~src:in_buf~dst:!out_buf());t.buffers<-[]in{copy_to;dest};;endmoduleBuffer_until_initialized=structtypestate=|Buffering_toofTemp_buffer.t|Needs_transferof{src:Temp_buffer.t;dst:(moduleWriter_intf.Destination)}|Setof(moduleWriter_intf.Destination)typet={mutablestate:state}letcreate()=lettemp_buffer=Temp_buffer.create()in{state=Buffering_totemp_buffer};;letset_destinationtdestination=matcht.statewith|Buffering_totemp_buffer->(* We can't immediately do the transfer because the writer is still using the last
buffer we gave it, so we need to wait for it to ask for a new buffer. *)t.state<-Needs_transfer{src=temp_buffer;dst=destination}|Needs_transfer_|Set_->failwith"Tried to set Buffer_until_initialized which already had destination";;letto_destinationt=letmoduleDest=structletnext_buf~ensure_capacity=let(moduleD)=matcht.statewith|Needs_transfer{src;dst}->src.copy_todst;t.state<-Setdst;dst|Buffering_totemp_buffer->temp_buffer.dest|Setd->dinD.next_buf~ensure_capacity;;letclose()=let(moduleD)=matcht.statewith|Needs_transfer{src;dst}->src.copy_todst;dst|Buffering_totemp_buffer->temp_buffer.dest|Setd->dinD.close();(* Make it so writes after closing will be gracefully ignored. *)t.state<-Set(black_hole_destination~len:1024~touch_memory:false);;endin(moduleDest:Writer_intf.Destination);;end