open SequenceSig (*-----------------------------------------------------------------------------*) module Make (Chunk : SequenceSig.FixedCapacityS) (Middle : SequenceSig.S) = struct (*-----------------------------------------------------------------------------*) (** Representation *) type 'a t = { mutable front_outer : 'a Chunk.t; mutable front_inner : 'a Chunk.t; mutable middle : ('a Chunk.t) Middle.t; mutable back_inner : 'a Chunk.t; mutable back_outer : 'a Chunk.t; } (** Creation *) let create () = { front_outer = Chunk.create(); front_inner = Chunk.create(); middle = Middle.create(); back_inner = Chunk.create(); back_outer = Chunk.create(); } (** Emptiness test *) let is_empty s = Chunk.is_empty s.front_outer && Chunk.is_empty s.front_inner && Middle.is_empty s.middle && Chunk.is_empty s.back_inner && Chunk.is_empty s.back_outer (** Push front *) let push_front x s = let co = s.front_outer in if Chunk.is_full co then begin let ci = s.front_inner in s.front_inner <- co; if Chunk.is_empty ci then begin s.front_outer <- ci; end else begin Middle.push_front ci s.middle; s.front_outer <- Chunk.create(); end end; Chunk.push_front x s.front_outer (** Push back *) let push_back x s = let co = s.back_outer in if Chunk.is_full co then begin let ci = s.back_inner in s.back_inner <- co; if Chunk.is_empty ci then begin s.back_outer <- ci; end else begin Middle.push_back ci s.middle; s.back_outer <- Chunk.create(); end end; Chunk.push_back x s.back_outer (** Pop front *) let pop_front s = let co = s.front_outer in if not (Chunk.is_empty co) then begin Chunk.pop_front co end else begin let ci = s.front_inner in let m = s.middle in if not (Chunk.is_empty ci) then begin s.front_outer <- ci; s.front_inner <- co; Chunk.pop_front s.front_outer end else if not (Middle.is_empty m) then begin s.front_outer <- Middle.pop_front m; Chunk.pop_front s.front_outer end else if not (Chunk.is_empty s.back_inner) then begin s.front_outer <- s.back_inner; s.back_inner <- co; Chunk.pop_front s.front_outer end else begin Chunk.pop_front s.back_outer end end (** Pop back *) let pop_back s = let co = s.back_outer in if not (Chunk.is_empty co) then begin Chunk.pop_back co end else begin let ci = s.back_inner in let m = s.middle in if not (Chunk.is_empty ci) then begin s.back_outer <- ci; s.back_inner <- co; Chunk.pop_back s.back_outer end else if not (Middle.is_empty m) then begin s.back_outer <- Middle.pop_back m; Chunk.pop_back s.back_outer end else if not (Chunk.is_empty s.front_inner) then begin s.back_outer <- s.front_inner; s.front_inner <- co; Chunk.pop_back s.back_outer end else begin Chunk.pop_back s.front_outer end end (** Push a buffer to the back of the middle sequence, possibly merging it with the back chunk in the middle sequence *) let middle_merge_back m c = let sc = Chunk.length c in if sc > 0 then begin if Middle.is_empty m then begin Middle.push_back c m end else begin let b = Middle.pop_back m in let sb = Chunk.length b in if sc + sb > Chunk.capacity then begin Middle.push_back b m; Middle.push_back c m end else begin Chunk.append b c; Middle.push_back b m; end end end (** Symmetric to middle_merge_back *) let middle_merge_front m c = let sc = Chunk.length c in if sc > 0 then begin if Middle.is_empty m then begin Middle.push_front c m end else begin let b = Middle.pop_front m in let sb = Chunk.length b in if sc + sb > Chunk.capacity then begin Middle.push_front b m; Middle.push_front c m end else begin Chunk.append c b; Middle.push_front c m; end end end (** Append to the back of s1 the items of s2; s2 becomes invalid *) let append s1 s2 = let m1 = s1.middle in let ci = s1.back_inner in let co = s1.back_outer in if Chunk.is_empty ci then begin middle_merge_back m1 co end else begin Middle.push_back ci m1; if not (Chunk.is_empty co) then Middle.push_back co m1; end; let m2 = s2.middle in let fi = s2.front_inner in let fo = s2.front_outer in if Chunk.is_empty fi then begin middle_merge_front m2 fo end else begin Middle.push_front fi m2; if not (Chunk.is_empty fo) then Middle.push_front fo m2; end; s1.back_inner <- s2.back_inner; s1.back_outer <- s2.back_outer; if not (Middle.is_empty m1) && not (Middle.is_empty m2) then begin let c1 = Middle.pop_back m1 in let sc1 = Chunk.length c1 in let c2 = Middle.pop_front m2 in let sc2 = Chunk.length c2 in if sc1 + sc2 > Chunk.capacity then begin Middle.push_back c1 m1; Middle.push_front c2 m2; end else begin Chunk.append c1 c2; Middle.push_back c1 m1; end end; Middle.append m1 m2 end |