123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206(*
* Copyright (c) 2018-2022 Tarides <contact@tarides.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*)open!ImportincludeIo_legacy_intfmoduleUnix:S=structmoduleRaw=Index_unix.Private.Rawtypet={file:string;raw:Raw.t;mutableoffset:int63;mutableflushed:int63;readonly:bool;mutableversion:Version.t;buf:Buffer.t;}letnamet=t.fileletheader_size=(* offset + version *)Int63.of_int16letunsafe_flusht=[%log.debug"IO flush %s"t.file];letbuf=Buffer.contentst.bufinifbuf=""then()elseletoffset=t.offsetinBuffer.cleart.buf;Raw.unsafe_writet.raw~off:t.flushedbuf0(String.lengthbuf);Raw.Offset.sett.rawoffset;letopenInt63.Syntaxin(* concurrent append might happen so here t.offset might differ
from offset *)ifnot(t.flushed+Int63.of_int(String.lengthbuf)=header_size+offset)thenFmt.failwith"reload error: %s flushed=%a offset+header=%a\n%!"t.fileInt63.ppt.flushedInt63.pp(offset+header_size);t.flushed<-offset+header_sizeletflusht=ift.readonlythenraiseIrmin_pack.RO_not_allowed;unsafe_flushtletauto_flush_limit=Int63.of_int1_000_000letappendtbuf=Buffer.add_stringt.bufbuf;letlen=Int63.of_int(String.lengthbuf)inletopenInt63.Syntaxint.offset<-t.offset+len;ift.offset-t.flushed>auto_flush_limitthenflushtletsett~offbuf=ift.readonlythenraiseIrmin_pack.RO_not_allowed;unsafe_flusht;letbuf_len=String.lengthbufinletopenInt63.SyntaxinRaw.unsafe_writet.raw~off:(header_size+off)buf0buf_len;assert(letlen=Int63.of_intbuf_leninletoff=header_size+off+leninoff<=t.flushed)exceptionInvalid_readofstringletraise_invalid_readfmt=Fmt.kstr(funs->raise(Invalid_reads))fmtletread_buffert~off~buf~len=letopenInt63.Syntaxinletoff=header_size+offinif(nott.readonly)&&off>t.flushedthenraise_invalid_read"Requested read of %d bytes at offset %a, but only flushed to %a"lenInt63.ppoffInt63.ppt.flushed;Raw.unsafe_readt.raw~off~lenbufletreadt~offbuf=read_buffert~off~buf~len:(Bytes.lengthbuf)letoffsett=t.offsetletforce_offsett=t.offset<-Raw.Offset.gett.raw;t.offsetletversiont=[%log.debug"[%s] version: %a"(Filename.basenamet.file)Version.ppt.version];t.versionletset_versiontv=[%log.debug"[%s] set_version: %a -> %a"(Filename.basenamet.file)Version.ppt.versionVersion.ppv];Raw.Version.sett.raw(Version.to_binv);t.version<-vletreadonlyt=t.readonlyletprotect_unix_exn=function|Unix.Unix_error_ase->failwith(Printexc.to_stringe)|e->raiseeletignore_enoent=function|Unix.Unix_error(Unix.ENOENT,_,_)->()|e->raiseeletprotectfx=tryfxwithe->protect_unix_exneletsafefx=tryfxwithe->ignore_enoenteletmkdirdirname=letrecauxdirk=ifSys.file_existsdir&&Sys.is_directorydirthenk()else(ifSys.file_existsdirthensafeUnix.unlinkdir;(aux[@tailcall])(Filename.dirnamedir)(fun()->protect(Unix.mkdirdir)0o755;k()))inauxdirname(fun()->())letraw~flags~version~offsetfile=letx=Unix.openfilefileflags0o644inletraw=Raw.vxinletheader={Raw.Header_prefix.version=Version.to_binversion;offset}inRaw.Header_prefix.setrawheader;rawletv~version~fresh~readonlyfile=letget_version()=matchversionwith|Somev->v|None->Fmt.invalid_arg"Must supply an explicit version when creating a new store ({ file \
= %s })"fileinletv~offset~versionraw={version;file;offset;raw;readonly;buf=Buffer.create(4*1024);flushed=Int63.Syntax.(header_size+offset);}inletmode=Unix.(ifreadonlythenO_RDONLYelseO_RDWR)inmkdir(Filename.dirnamefile);matchSys.file_existsfilewith|false->letversion=get_version()inletraw=raw~flags:[O_CREAT;mode;O_CLOEXEC]~version~offset:Int63.zerofileinv~offset:Int63.zero~versionraw|true->letx=Unix.openfilefileUnix.[O_EXCL;mode;O_CLOEXEC]0o644inletraw=Raw.vxiniffreshthen(letversion=get_version()inletheader={Raw.Header_prefix.version=Version.to_binversion;offset=Int63.zero;}inRaw.Header_prefix.setrawheader;v~offset:Int63.zero~versionraw)elseletactual_version=letv_string=Raw.Version.getrawinmatchVersion.of_binv_stringwith|Somev->v|None->Version.invalid_argv_stringin(matchversionwith|SomevwhenVersion.compareactual_versionv>0->raise(Version.Invalid{expected=v;found=actual_version})|_->());letoffset=Raw.Offset.getrawinv~offset~version:actual_versionrawletcloset=Raw.closet.rawletexistsfile=Sys.file_existsfileletsize{raw;_}=(Raw.fstatraw).st_sizeend