From 5681f4f3528c005166a6115ce4b913fcd8b19bb0 Mon Sep 17 00:00:00 2001 From: Simon Martens Date: Sun, 16 Mar 2025 21:10:52 +0100 Subject: [PATCH] Iterators in XMLProvider doing the locking --- xml/xmlprovider.go | 55 +++++++++++++++++++++++++++++--------------- xmlmodels/letter.go | 11 +++++---- xmlmodels/library.go | 22 +++++++++++++----- 3 files changed, 59 insertions(+), 29 deletions(-) diff --git a/xml/xmlprovider.go b/xml/xmlprovider.go index a49daf9..2fa43d2 100644 --- a/xml/xmlprovider.go +++ b/xml/xmlprovider.go @@ -1,6 +1,7 @@ package xmlparsing import ( + "iter" "slices" "sync" "time" @@ -36,7 +37,6 @@ type XMLParser[T IXMLItem] struct { // INFO: map is type map[string]*T Items sync.Map // INFO: map is type [string]ItemInfo - // It keeps information about parsing status of the items. Infos sync.Map // INFO: Resolver is used to resolve references (back-links) between XML items. @@ -44,8 +44,7 @@ type XMLParser[T IXMLItem] struct { mu sync.RWMutex // TODO: This array is meant to be for iteration purposes, since iteration over the sync.Map is slow. - // It is best for this array to be sorted by key of the corresponding item. - Array []T + array []T } func NewXMLParser[T IXMLItem]() *XMLParser[T] { @@ -59,7 +58,7 @@ func (p *XMLParser[T]) Prepare() { p.mu.Lock() defer p.mu.Unlock() - p.Array = make([]T, 0, len(p.Array)) + p.array = make([]T, 0, len(p.array)) p.Resolver.Clear() } @@ -82,7 +81,7 @@ func (p *XMLParser[T]) Serialize(dataholder XMLRootElement[T], path string, late p.mu.Lock() defer p.mu.Unlock() - p.Array = append(p.Array, newItems...) + p.array = append(p.array, newItems...) return nil } @@ -118,7 +117,7 @@ func (p *XMLParser[T]) Cleanup(latest ParseMeta) { p.mu.Lock() defer p.mu.Unlock() for _, item := range toappend { - p.Array = append(p.Array, *item) + p.array = append(p.array, *item) p.addResolvable(*item) } } @@ -151,8 +150,10 @@ func (p *XMLParser[T]) ReverseLookup(item IXMLItem) []Resolved[T] { } func (a *XMLParser[T]) String() string { + a.RLock() + defer a.RUnlock() var s string - for _, item := range a.Array { + for _, item := range a.array { s += item.String() } return s @@ -176,23 +177,41 @@ func (p *XMLParser[T]) Item(id string) *T { return i } -func (p *XMLParser[T]) Find(fn func(*T) bool) []T { - p.mu.RLock() - defer p.mu.RUnlock() - var items []T - for _, item := range p.Array { - if fn(&item) { - items = append(items, item) +func (p *XMLParser[T]) Filter(f func(T) bool) iter.Seq[T] { + return func(yield func(T) bool) { + p.mu.RLock() + defer p.mu.RUnlock() + for _, v := range p.array { + if f(v) && !yield(v) { + return + } } } - return items } -// INFO: These are only reading locks. -func (p *XMLParser[T]) Lock() { +func (p *XMLParser[T]) Iterate() iter.Seq[T] { + return func(yield func(T) bool) { + p.mu.RLock() + defer p.mu.RUnlock() + for _, v := range p.array { + if !yield(v) { + return + } + } + } +} + +func (p *XMLParser[T]) Count() int { + p.RLock() + defer p.RUnlock() + return len(p.array) +} + +// INFO: These are reading locks. +func (p *XMLParser[T]) RLock() { p.mu.RLock() } -func (p *XMLParser[T]) Unlock() { +func (p *XMLParser[T]) RUnlock() { p.mu.RUnlock() } diff --git a/xmlmodels/letter.go b/xmlmodels/letter.go index 913c0d8..ffb7d72 100644 --- a/xmlmodels/letter.go +++ b/xmlmodels/letter.go @@ -6,11 +6,12 @@ import ( ) type Letter struct { - XMLName xml.Name `xml:"letterText"` - Letter int `xml:"letter,attr"` - Pages []Page `xml:"page"` - Hands []RefElement `xml:"hand"` - Content string `xml:",innerxml"` + XMLName xml.Name `xml:"letterText"` + Letter int `xml:"letter,attr"` + Pages []Page `xml:"page"` + Hands []RefElement `xml:"hand"` + Content string `xml:",innerxml"` + Chardata string `xml:",chardata"` } func (l Letter) Keys() []any { diff --git a/xmlmodels/library.go b/xmlmodels/library.go index 4422af4..8269a85 100644 --- a/xmlmodels/library.go +++ b/xmlmodels/library.go @@ -37,27 +37,37 @@ func (l *Library) String() string { sb := strings.Builder{} sb.WriteString("Persons: ") - sb.WriteString(strconv.Itoa(len(l.Persons.Array))) + sb.WriteString(strconv.Itoa(l.Persons.Count())) sb.WriteString("\n") sb.WriteString("Places: ") - sb.WriteString(strconv.Itoa(len(l.Places.Array))) + sb.WriteString(strconv.Itoa(l.Places.Count())) sb.WriteString("\n") sb.WriteString("AppDefs: ") - sb.WriteString(strconv.Itoa(len(l.AppDefs.Array))) + sb.WriteString(strconv.Itoa(l.AppDefs.Count())) sb.WriteString("\n") sb.WriteString("Letters: ") - sb.WriteString(strconv.Itoa(len(l.Letters.Array))) + sb.WriteString(strconv.Itoa(l.Letters.Count())) + filter := func(item Letter) bool { + return len(item.Hands) > 0 + } + hands := 0 + for l := range l.Letters.Filter(filter) { + hands += 1 + sb.WriteString("\n") + sb.WriteString(strconv.Itoa(l.Letter) + ": ") + sb.WriteString(strconv.Itoa(len(l.Hands)) + " Hände, No " + strconv.Itoa(hands)) + } sb.WriteString("\n") sb.WriteString("Traditions: ") - sb.WriteString(strconv.Itoa(len(l.Traditions.Array))) + sb.WriteString(strconv.Itoa(l.Traditions.Count())) sb.WriteString("\n") sb.WriteString("Metas: ") - sb.WriteString(strconv.Itoa(len(l.Metas.Array))) + sb.WriteString(strconv.Itoa(l.Metas.Count())) sb.WriteString("\n") return sb.String()