util.go (112 lines of code) (raw):

// Licensed to Elasticsearch B.V. under one or more contributor // license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright // ownership. Elasticsearch B.V. licenses this file to you under // the Apache License, Version 2.0 (the "License"); you may // not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. package txfile import ( "math/bits" ) // pagingWriter supports writing entries into a linked (pre-allocated) list of // pages. type pagingWriter struct { ids idList buf []byte pageSize uint extraHeader uint onPage func(id PageID, buf []byte) reason // current page state i int off uint hdr *listPage page []byte payload []byte count uint32 } const maxUint uint = ^uint(0) func newPagingWriter( ids idList, pageSize uint, extraHeader uint, onPage func(id PageID, buf []byte) reason, ) *pagingWriter { if len(ids) == 0 { return nil } buf := make([]byte, len(ids)*int(pageSize)) // prelink all pages, in case some are not written to off := 0 for _, id := range ids[1:] { hdr, _ := castListPage(buf[off:]) hdr.next.Set(id) off += int(pageSize) } w := &pagingWriter{ ids: ids, buf: buf, pageSize: pageSize, extraHeader: extraHeader, onPage: onPage, } w.prepareNext("") return w } func (w *pagingWriter) Write(entry []byte) reason { const op = "txfile/write-meta-list" if w == nil { return nil } if len(w.payload) < len(entry) { if err := w.flushCurrent(op); err != nil { return err } } n := copy(w.payload, entry) w.payload = w.payload[n:] w.count++ return nil } func (w *pagingWriter) Flush() reason { const op = "txfile/flush-meta-list" if w == nil { return nil } if err := w.finalizePage(); err != nil { return err } for w.i < len(w.ids) { // update to next page w.prepareNext(op) if err := w.finalizePage(); err != nil { return err } } return nil } func (w *pagingWriter) flushCurrent(op string) (err reason) { if err = w.finalizePage(); err == nil { err = w.prepareNext(op) } return } func (w *pagingWriter) finalizePage() reason { w.hdr.count.Set(w.count) if w.onPage != nil { if err := w.onPage(w.ids[w.i], w.page); err != nil { return err } } w.count = 0 w.off += w.pageSize w.i++ return nil } func (w *pagingWriter) prepareNext(op string) reason { if w.i >= len(w.ids) { return errOp(op).of(InternalError).report("Not enough pages pre-allocated") } w.page = w.buf[w.off : w.off+w.pageSize] w.hdr, w.payload = castListPage(w.page) w.payload = w.payload[w.extraHeader:] return nil } func isPowerOf2(v uint64) bool { // an uint is a power of two if exactly one bit is set -> return v > 0 && (v&(v-1)) == 0 } // nextPowerOf2 computes the next power of two value of `u`, such that // nextPowerOf2(u) > u // The input value must not have the highest bit being set. func nextPowerOf2(u uint64) uint64 { b := uint64(bits.LeadingZeros64(u)) return uint64(1) << (64 - b) } func ignoreReason(fn func() reason) func() { return func() { _ = fn() } }