Commit 0aff75b0 authored by Kirill Smelkov's avatar Kirill Smelkov

.

parent 87f373f5
...@@ -153,8 +153,8 @@ func (M *RangedMap) SetRange(r KeyRange, v VALUE) { ...@@ -153,8 +153,8 @@ func (M *RangedMap) SetRange(r KeyRange, v VALUE) {
// if [r.lo,r.hi) was outside of any entry - create new entry // if [r.lo,r.hi) was outside of any entry - create new entry
if r.Hi_ < M.entryv[ilo].Lo { if r.Hi_ < M.entryv[ilo].Lo {
vInsert(&M.entryv, ilo, r) vInsert(&M.entryv, ilo, e)
debugfRMap("\tinsert %s\t-> %s\n", r, M) debugfRMap("\tinsert %s\t-> %s\n", e, M)
} }
// now we have covered entries merged as needed into [ilo] // now we have covered entries merged as needed into [ilo]
......
// Copyright (C) 2021 Nexedi SA and Contributors.
// Kirill Smelkov <kirr@nexedi.com>
//
// This program is free software: you can Use, Study, Modify and Redistribute
// it under the terms of the GNU General Public License version 3, or (at your
// option) any later version, as published by the Free Software Foundation.
//
// You can also Link and Combine this program with other software covered by
// the terms of any of the Free Software licenses or any of the Open Source
// Initiative approved licenses and Convey the resulting work. Corresponding
// source of such a combination shall include the source code for all other
// software used.
//
// This program is distributed WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//
// See COPYING file for full licensing terms.
// See https://www.nexedi.com/licensing for rationale and options.
package blib
//go:generate ./gen-rangemap _RangedMap_str string zrangemap_str.go
import (
"testing"
)
func TestRangeMap(t *testing.T) {
// XXX
}
// Code generated by gen-rangemap _RangedMap_str string; DO NOT EDIT.
// Copyright (C) 2021 Nexedi SA and Contributors.
// Kirill Smelkov <kirr@nexedi.com>
//
// This program is free software: you can Use, Study, Modify and Redistribute
// it under the terms of the GNU General Public License version 3, or (at your
// option) any later version, as published by the Free Software Foundation.
//
// You can also Link and Combine this program with other software covered by
// the terms of any of the Free Software licenses or any of the Open Source
// Initiative approved licenses and Convey the resulting work. Corresponding
// source of such a combination shall include the source code for all other
// software used.
//
// This program is distributed WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//
// See COPYING file for full licensing terms.
// See https://www.nexedi.com/licensing for rationale and options.
package blib
// map [lo,hi) Key ranges to values.
import (
"fmt"
"sort"
)
const trace_RangedMap_str = true
const debug_RangedMap_str = true
// _RangedMap_str is Key->string map with adjacent keys mapped to the same value coalesced into Ranges.
//
// Zero value represents empty map.
type _RangedMap_str struct {
// TODO rework to use BTree lo->hi_ instead if in practice in treediff,
// and other usage places, N(ranges) turns out to be not small
// (i.e. performance turns out to be not acceptable)
entryv []_RangedMap_strEntry // lo↑
}
// _RangedMap_strEntry represents one entry in _RangedMap_str.
type _RangedMap_strEntry struct {
KeyRange
Value string
}
// Get returns value associated with key k.
func (M *_RangedMap_str) Get(k Key) string {
v, _ := M.Get_(k)
return v
}
// Set changes M to map key k to value v.
func (M *_RangedMap_str) Set(k Key, v string) {
M.SetRange(KeyRange{Lo: k, Hi_: k}, v)
}
// Del removes key k.
func (M *_RangedMap_str) Del(k Key) {
M.DelRange(KeyRange{Lo: k, Hi_: k})
}
// Has returns whether key k is present in the map.
func (M *_RangedMap_str) Has(k Key) bool {
_, ok := M.Get_(k)
return ok
}
// Get_ is comma-ok version of Get.
func (M *_RangedMap_str) Get_(k Key) (v string, ok bool) {
if trace_RangedMap_str {
fmt.Printf("\n\nGet_:\n")
fmt.Printf(" M: %s\n", M)
fmt.Printf(" k: %s\n", k)
defer func() {
fmt.Printf("->·: %v, %t\n", v, ok)
}()
}
M.verify()
// find first ilo: k < [ilo].hi
l := len(M.entryv)
ilo := sort.Search(l, func(i int) bool {
return k <= M.entryv[i].Hi_
})
debugf_RangedMap_str("\tilo: %d\n", ilo)
if ilo == l { // not found
return
}
e := M.entryv[ilo]
if !(e.Lo <= k) { // not found
return
}
// found
return e.Value, true
}
// SetRange changes M to map key range r to value v.
func (M *_RangedMap_str) SetRange(r KeyRange, v string) {
e := _RangedMap_strEntry{r,v}
if trace_RangedMap_str {
fmt.Printf("\n\nSetRange:\n")
fmt.Printf(" M: %s\n", M)
fmt.Printf(" e: %s\n", e)
defer fmt.Printf("->u: %s\n", M)
}
M.verify()
defer M.verify()
// find first ilo: r.Lo < [ilo].hi
l := len(M.entryv)
ilo := sort.Search(l, func(i int) bool {
return r.Lo <= M.entryv[i].Hi_
})
debugf_RangedMap_str("\tilo: %d\n", ilo)
if ilo == l { // not found
M.entryv = append(M.entryv, e)
l++
debugf_RangedMap_str("\tappend %s\t-> %s\n", e, M)
}
// find last jhi: [jhi].Lo < r.hi
jhi := ilo
for ;; jhi++ {
if jhi == l {
break
}
if M.entryv[jhi].Lo <= r.Hi_ {
continue
}
break
}
debugf_RangedMap_str("\tjhi: %d\n", jhi)
// entries in [ilo:jhi) ∈ [r.Lo,r.hi) and should be merged into one
// XXX check different values
if (jhi - ilo) > 1 {
lo := M.entryv[ilo].Lo
hi_ := M.entryv[jhi-1].Hi_
vReplaceSlice__RangedMap_str(&M.entryv, ilo,jhi, KeyRange{lo,hi_})
debugf_RangedMap_str("\tmerge M[%d:%d]\t-> %s\n", ilo, jhi, M)
}
jhi = -1 // no longer valid
// if [r.lo,r.hi) was outside of any entry - create new entry
if r.Hi_ < M.entryv[ilo].Lo {
vInsert__RangedMap_str(&M.entryv, ilo, e)
debugf_RangedMap_str("\tinsert %s\t-> %s\n", e, M)
}
// now we have covered entries merged as needed into [ilo]
// extend this entry if r coverage is wider
if r.Lo < M.entryv[ilo].Lo {
M.entryv[ilo].Lo = r.Lo
debugf_RangedMap_str("\textend left\t-> %s\n", M)
}
if r.Hi_ > M.entryv[ilo].Hi_ {
M.entryv[ilo].Hi_ = r.Hi_
debugf_RangedMap_str("\textend right\t-> %s\n", M)
}
// and check if we should merge it with right/left neighbours
if ilo+1 < len(M.entryv) { // right
if M.entryv[ilo].Hi_+1 == M.entryv[ilo+1].Lo { // XXX && .Value same
vReplaceSlice__RangedMap_str(&M.entryv, ilo,ilo+2,
KeyRange{M.entryv[ilo].Lo, M.entryv[ilo+1].Hi_})
debugf_RangedMap_str("\tmerge right\t-> %s\n", M)
}
}
if ilo > 0 { // left
if M.entryv[ilo-1].Hi_+1 == M.entryv[ilo].Lo { // XXX && .Value same
vReplaceSlice__RangedMap_str(&M.entryv, ilo-1,ilo+1,
KeyRange{M.entryv[ilo-1].Lo, M.entryv[ilo].Hi_})
debugf_RangedMap_str("\tmerge left\t-> %s\n", M)
}
}
// done
}
// DelRange removes range r from the map.
func (M *_RangedMap_str) DelRange(r KeyRange) {
if trace_RangedMap_str {
fmt.Printf("\n\nDelRange:\n")
fmt.Printf(" M: %s\n", M)
fmt.Printf(" r: %s\n", r)
defer fmt.Printf("->d: %s\n", M)
}
M.verify()
defer M.verify()
// find first ilo: r.Lo < [ilo].hi
l := len(M.entryv)
ilo := sort.Search(l, func(i int) bool {
return r.Lo <= M.entryv[i].Hi_
})
debugf_RangedMap_str("\tilo: %d\n", ilo)
if ilo == l { // not found
debugf_RangedMap_str("\tnon-overlap right\n")
return
}
// find last jhi: [jhi].Lo < r.hi
jhi := ilo
for ;; jhi++ {
if jhi == l {
break
}
if M.entryv[jhi].Lo <= r.Hi_ {
continue
}
break
}
debugf_RangedMap_str("\tjhi: %d\n", jhi)
if jhi == 0 {
debugf_RangedMap_str("\tnon-overlap left\n")
return
}
// [ilo+1:jhi-1] should be deleted
// [ilo] and [jhi-1] overlap with [r.lo,r.hi) - they should be deleted, or shrinked,
// or split+shrinked if ilo==jhi-1 and r is inside [ilo]
if jhi-ilo == 1 && M.entryv[ilo].Lo < r.Lo && r.Hi_ < M.entryv[ilo].Hi_ {
x := M.entryv[ilo]
vInsert__RangedMap_str(&M.entryv, ilo, x)
jhi++
debugf_RangedMap_str("\tpresplit copy %s\t-> %s\n", x, M)
}
if M.entryv[ilo].Lo < r.Lo { // shrink left
M.entryv[ilo].Hi_ = r.Lo-1
ilo++
debugf_RangedMap_str("\tshrink [%d] left\t-> %s\n", ilo, M)
}
if r.Hi_ < M.entryv[jhi-1].Hi_ { // shrink right
M.entryv[jhi-1].Lo = r.Hi_+1
jhi--
debugf_RangedMap_str("\tshrink [%d] right\t-> %s\n", jhi-1, M)
}
if (jhi - ilo) > 0 {
vDeleteSlice__RangedMap_str(&M.entryv, ilo,jhi)
debugf_RangedMap_str("\tdelete M[%d:%d]\t-> %s\n", ilo, jhi, M)
}
// done
}
// HasRange returns whether all keys from range r belong to the map.
func (M *_RangedMap_str) HasRange(r KeyRange) (yes bool) {
if trace_RangedMap_str {
fmt.Printf("\n\nHasRange:\n")
fmt.Printf(" M: %s\n", M)
fmt.Printf(" r: %s\n", r)
defer func() {
fmt.Printf("->·: %v\n", yes)
}()
}
M.verify()
// find first ilo: r.lo < [ilo].hi
l := len(M.entryv)
ilo := sort.Search(l, func(i int) bool {
return r.Lo <= M.entryv[i].Hi_
})
debugf_RangedMap_str("\tilo: %d\n", ilo)
if ilo == l { // not found
return false
}
// all keys from r are in M if r ∈ [ilo] XXX not in case of different values
return (M.entryv[ilo].Lo <= r.Lo && r.Hi_ <= M.entryv[ilo].Hi_)
}
// --------
// verify checks _RangedMap_str for internal consistency:
// - ranges must be not overlapping and ↑
// - adjacent ranges must map to different values
func (M *_RangedMap_str) verify() {
// TODO !debug -> return
var badv []string
badf := func(format string, argv ...interface{}) {
badv = append(badv, fmt.Sprintf(format, argv...))
}
defer func() {
if badv != nil {
emsg := "M.verify: fail:\n\n"
for _, bad := range badv {
emsg += fmt.Sprintf("- %s\n", bad)
}
emsg += fmt.Sprintf("\nS: %s\n", M)
panic(emsg)
}
}()
hi_Prev := KeyMin
var v_Prev string
for i, e := range M.entryv {
hiPrev := hi_Prev + 1
if i > 0 {
if (e.Value == v_Prev) {
if !(hiPrev < e.Lo) { // NOTE not ≤ - adjacent ranges must be merged
badf("[%d]: same value: !(hiPrev < e.lo)", i)
}
} else {
if !(hi_Prev <= e.Lo) {
badf("[%d]: different value: !(hiPrev ≤ e.lo)", i)
}
}
}
if !(e.Lo <= e.Hi_) {
badf("[%d]: !(e.lo ≤ e.hi_)", i)
}
hi_Prev = e.Hi_
v_Prev = e.Value
}
}
// Clone returns copy of the map.
//
// NOTE values are _not_ cloned.
func (orig *_RangedMap_str) Clone() *_RangedMap_str {
klon := &_RangedMap_str{}
klon.entryv = append(klon.entryv, orig.entryv...)
return klon
}
// Empty returns whether the map is empty.
func (M *_RangedMap_str) Empty() bool {
return len(M.entryv) == 0
}
// Equal returns whether A == B.
func (A *_RangedMap_str) Equal(B *_RangedMap_str) bool {
if len(A.entryv) != len(B.entryv) {
return false
}
for i, ea := range A.entryv {
eb := B.entryv[i]
if ea != eb {
return false
}
}
return true
}
// Clear removes all elements from the map.
func (M *_RangedMap_str) Clear() {
M.entryv = nil
}
// AllRanges returns slice of all key ranges in the set.
//
// TODO -> iter?
func (M *_RangedMap_str) AllRanges() /*readonly*/[]_RangedMap_strEntry {
return M.entryv
}
// XXX -> ptr?
func (M _RangedMap_str) String() string {
s := "{"
for i, e := range M.entryv {
if i > 0 {
s += " "
}
s += e.String()
}
s += "}"
return s
}
func (e *_RangedMap_strEntry) String() string {
s := e.KeyRange.String()
v := fmt.Sprintf("%v", e.Value)
if v != "" { // omit ":<v>" in the case of set
s += ":" + v
}
return s
}
func debugf_RangedMap_str(format string, argv ...interface{}) {
if !debug_RangedMap_str {
return
}
fmt.Printf(format, argv...)
}
// ---- slice ops ----
// vInsert__RangedMap_str inserts e into *pv[i].
func vInsert__RangedMap_str(pv *[]_RangedMap_strEntry, i int, e _RangedMap_strEntry) {
v := *pv
v = append(v, _RangedMap_strEntry{})
copy(v[i+1:], v[i:])
v[i] = e
*pv = v
}
// vDeleteSlice__RangedMap_str deletes *pv[lo:hi].
func vDeleteSlice__RangedMap_str(pv *[]_RangedMap_strEntry, lo,hi int) {
v := *pv
n := copy(v[lo:], v[hi:])
v = v[:lo+n]
*pv = v
}
// vReplaceSlice__RangedMap_str replaces *pv[lo:hi] with e.
func vReplaceSlice__RangedMap_str(pv *[]_RangedMap_strEntry, lo,hi int, e _RangedMap_strEntry) {
v := *pv
n := copy(v[lo+1:], v[hi:])
v[lo] = e
v = v[:lo+1+n]
*pv = v
}
...@@ -155,8 +155,8 @@ func (M *_RangedMap_void) SetRange(r KeyRange, v void) { ...@@ -155,8 +155,8 @@ func (M *_RangedMap_void) SetRange(r KeyRange, v void) {
// if [r.lo,r.hi) was outside of any entry - create new entry // if [r.lo,r.hi) was outside of any entry - create new entry
if r.Hi_ < M.entryv[ilo].Lo { if r.Hi_ < M.entryv[ilo].Lo {
vInsert__RangedMap_void(&M.entryv, ilo, r) vInsert__RangedMap_void(&M.entryv, ilo, e)
debugf_RangedMap_void("\tinsert %s\t-> %s\n", r, M) debugf_RangedMap_void("\tinsert %s\t-> %s\n", e, M)
} }
// now we have covered entries merged as needed into [ilo] // now we have covered entries merged as needed into [ilo]
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment