zry 2 years ago
parent
commit
9982d27520

+ 23 - 0
.idea/$CACHE_FILE$

@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectInspectionProfilesVisibleTreeState">
+    <entry key="Project Default">
+      <profile-state>
+        <expanded-state>
+          <State />
+          <State>
+            <id>General</id>
+          </State>
+          <State>
+            <id>XPath</id>
+          </State>
+        </expanded-state>
+        <selected-state>
+          <State>
+            <id>CSS</id>
+          </State>
+        </selected-state>
+      </profile-state>
+    </entry>
+  </component>
+</project>

+ 2 - 0
.idea/.gitignore

@@ -0,0 +1,2 @@
+# Default ignored files
+/workspace.xml

+ 8 - 0
.idea/backable-ringbuffer.iml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="WEB_MODULE" version="4">
+  <component name="NewModuleRootManager">
+    <content url="file://$MODULE_DIR$" />
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+  </component>
+</module>

+ 5 - 0
.idea/inspectionProfiles/profiles_settings.xml

@@ -0,0 +1,5 @@
+<component name="InspectionProjectProfileManager">
+  <settings>
+    <option name="PROJECT_PROFILE" />
+  </settings>
+</component>

+ 86 - 0
.idea/markdown-navigator.xml

@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="MarkdownProjectSettings" wasCopied="true">
+    <PreviewSettings splitEditorLayout="SPLIT" splitEditorPreview="PREVIEW" useGrayscaleRendering="false" zoomFactor="1.0" maxImageWidth="0" showGitHubPageIfSynced="false" allowBrowsingInPreview="false" synchronizePreviewPosition="true" highlightPreviewType="NONE" highlightFadeOut="5" highlightOnTyping="true" synchronizeSourcePosition="true" verticallyAlignSourceAndPreviewSyncPosition="true" showSearchHighlightsInPreview="false" showSelectionInPreview="true" openRemoteLinks="true" replaceUnicodeEmoji="false" lastLayoutSetsDefault="false">
+      <PanelProvider>
+        <provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.panel" providerName="Default - Swing" />
+      </PanelProvider>
+    </PreviewSettings>
+    <ParserSettings gitHubSyntaxChange="false" emojiShortcuts="1" emojiImages="0">
+      <PegdownExtensions>
+        <option name="ABBREVIATIONS" value="false" />
+        <option name="ANCHORLINKS" value="true" />
+        <option name="ASIDE" value="false" />
+        <option name="ATXHEADERSPACE" value="true" />
+        <option name="AUTOLINKS" value="true" />
+        <option name="DEFINITIONS" value="false" />
+        <option name="DEFINITION_BREAK_DOUBLE_BLANK_LINE" value="false" />
+        <option name="FENCED_CODE_BLOCKS" value="true" />
+        <option name="FOOTNOTES" value="false" />
+        <option name="HARDWRAPS" value="false" />
+        <option name="HTML_DEEP_PARSER" value="false" />
+        <option name="INSERTED" value="false" />
+        <option name="QUOTES" value="false" />
+        <option name="RELAXEDHRULES" value="true" />
+        <option name="SMARTS" value="false" />
+        <option name="STRIKETHROUGH" value="true" />
+        <option name="SUBSCRIPT" value="false" />
+        <option name="SUPERSCRIPT" value="false" />
+        <option name="SUPPRESS_HTML_BLOCKS" value="false" />
+        <option name="SUPPRESS_INLINE_HTML" value="false" />
+        <option name="TABLES" value="true" />
+        <option name="TASKLISTITEMS" value="true" />
+        <option name="TOC" value="false" />
+        <option name="WIKILINKS" value="true" />
+      </PegdownExtensions>
+      <ParserOptions>
+        <option name="ADMONITION_EXT" value="false" />
+        <option name="ATTRIBUTES_EXT" value="false" />
+        <option name="COMMONMARK_LISTS" value="true" />
+        <option name="DUMMY" value="false" />
+        <option name="EMOJI_SHORTCUTS" value="true" />
+        <option name="ENUMERATED_REFERENCES_EXT" value="false" />
+        <option name="FLEXMARK_FRONT_MATTER" value="false" />
+        <option name="GFM_LOOSE_BLANK_LINE_AFTER_ITEM_PARA" value="false" />
+        <option name="GFM_TABLE_RENDERING" value="true" />
+        <option name="GITBOOK_URL_ENCODING" value="false" />
+        <option name="GITHUB_LISTS" value="false" />
+        <option name="GITHUB_WIKI_LINKS" value="true" />
+        <option name="GITLAB_EXT" value="false" />
+        <option name="GITLAB_MATH_EXT" value="false" />
+        <option name="GITLAB_MERMAID_EXT" value="false" />
+        <option name="HEADER_ID_NON_ASCII_TO_LOWERCASE" value="false" />
+        <option name="HEADER_ID_NO_DUPED_DASHES" value="false" />
+        <option name="JEKYLL_FRONT_MATTER" value="false" />
+        <option name="MACROS_EXT" value="false" />
+        <option name="NO_TEXT_ATTRIBUTES" value="false" />
+        <option name="PARSE_HTML_ANCHOR_ID" value="false" />
+        <option name="PLANTUML_FENCED_CODE" value="false" />
+        <option name="PUML_FENCED_CODE" value="false" />
+        <option name="SIM_TOC_BLANK_LINE_SPACER" value="true" />
+      </ParserOptions>
+    </ParserSettings>
+    <HtmlSettings headerTopEnabled="false" headerBottomEnabled="false" bodyTopEnabled="false" bodyBottomEnabled="false" embedUrlContent="false" addPageHeader="true" embedImages="false" embedHttpImages="false" imageUriSerials="false" addDocTypeHtml="true" noParaTags="false" plantUmlConversion="0" mathConversion="-1">
+      <GeneratorProvider>
+        <provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.generator" providerName="Default Swing HTML Generator" />
+      </GeneratorProvider>
+      <headerTop />
+      <headerBottom />
+      <bodyTop />
+      <bodyBottom />
+    </HtmlSettings>
+    <CssSettings previewScheme="UI_SCHEME" cssUri="" isCssUriEnabled="false" isCssUriSerial="false" isCssTextEnabled="false" isDynamicPageWidth="true">
+      <StylesheetProvider>
+        <provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.css" providerName="Default Swing Stylesheet" />
+      </StylesheetProvider>
+      <ScriptProviders />
+      <cssText />
+      <cssUriHistory />
+    </CssSettings>
+    <AnnotatorSettings targetHasSpaces="true" linkCaseMismatch="true" wikiCaseMismatch="true" wikiLinkHasDashes="true" notUnderWikiHome="true" targetNotWikiPageExt="true" notUnderSourceWikiHome="true" targetNameHasAnchor="true" targetPathHasAnchor="true" wikiLinkHasSlash="true" wikiLinkHasSubdir="true" wikiLinkHasOnlyAnchor="true" linkTargetsWikiHasExt="true" linkTargetsWikiHasBadExt="true" notUnderSameRepo="true" targetNotUnderVcs="false" linkNeedsExt="true" linkHasBadExt="true" linkTargetNeedsExt="true" linkTargetHasBadExt="true" wikiLinkNotInWiki="true" imageTargetNotInRaw="true" repoRelativeAcrossVcsRoots="true" multipleWikiTargetsMatch="true" unresolvedLinkReference="true" linkIsIgnored="true" anchorIsIgnored="true" anchorIsUnresolved="true" anchorLineReferenceIsUnresolved="true" anchorLineReferenceFormat="true" anchorHasDuplicates="true" abbreviationDuplicates="true" abbreviationNotUsed="true" attributeIdDuplicateDefinition="true" attributeIdNotUsed="true" footnoteDuplicateDefinition="true" footnoteUnresolved="true" footnoteDuplicates="true" footnoteNotUsed="true" macroDuplicateDefinition="true" macroUnresolved="true" macroDuplicates="true" macroNotUsed="true" referenceDuplicateDefinition="true" referenceUnresolved="true" referenceDuplicates="true" referenceNotUsed="true" referenceUnresolvedNumericId="true" enumRefDuplicateDefinition="true" enumRefUnresolved="true" enumRefDuplicates="true" enumRefNotUsed="true" enumRefLinkUnresolved="true" enumRefLinkDuplicates="true" simTocUpdateNeeded="true" simTocTitleSpaceNeeded="true" />
+    <HtmlExportSettings updateOnSave="false" parentDir="" targetDir="" cssDir="css" scriptDir="js" plainHtml="false" imageDir="" copyLinkedImages="false" imageUniquifyType="0" targetPathType="2" targetExt="" useTargetExt="false" noCssNoScripts="false" useElementStyleAttribute="false" linkToExportedHtml="true" exportOnSettingsChange="true" regenerateOnProjectOpen="false" linkFormatType="HTTP_ABSOLUTE" />
+    <LinkMapSettings>
+      <textMaps />
+    </LinkMapSettings>
+  </component>
+</project>

+ 6 - 0
.idea/misc.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="JavaScriptSettings">
+    <option name="languageLevel" value="ES6" />
+  </component>
+</project>

+ 8 - 0
.idea/modules.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectModuleManager">
+    <modules>
+      <module fileurl="file://$PROJECT_DIR$/.idea/backable-ringbuffer.iml" filepath="$PROJECT_DIR$/.idea/backable-ringbuffer.iml" />
+    </modules>
+  </component>
+</project>

+ 6 - 0
.idea/vcs.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="VcsDirectoryMappings">
+    <mapping directory="$PROJECT_DIR$" vcs="Git" />
+  </component>
+</project>

+ 1 - 1
README.md

@@ -1,3 +1,3 @@
 # backable-ringbuffer
 
-A backable bytes ring buffer implementation based on github.com/gammazero/deque
+A backable bytes ring buffer implementation (Modified from github.com/smallnest/ringbuffer).

+ 3 - 0
go.mod

@@ -0,0 +1,3 @@
+module git.swzry.com/zry/backable-ringbuffer
+
+go 1.16

+ 57 - 0
mutex.go

@@ -0,0 +1,57 @@
+package backable_ringbuffer
+
+// Copied from github.com/smallnest/ringbuffer
+
+import (
+	"sync"
+	"sync/atomic"
+	"unsafe"
+)
+
+const (
+	mutexLocked = 1 << iota // mutex is locked
+	mutexWoken
+	mutexStarving
+	mutexWaiterShift = iota
+)
+
+// Mutex is a locker which supports TryLock.
+type Mutex struct {
+	sync.Mutex
+}
+
+func (m *Mutex) TryLock() bool {
+	if atomic.CompareAndSwapInt32((*int32)(unsafe.Pointer(&m.Mutex)), 0, mutexLocked) {
+		return true
+	}
+
+	old := atomic.LoadInt32((*int32)(unsafe.Pointer(&m.Mutex)))
+	if old&(mutexLocked|mutexStarving|mutexWoken) != 0 {
+		return false
+	}
+
+	new := old | mutexLocked
+	return atomic.CompareAndSwapInt32((*int32)(unsafe.Pointer(&m.Mutex)), old, new)
+}
+
+func (m *Mutex) Count() int {
+	v := atomic.LoadInt32((*int32)(unsafe.Pointer(&m.Mutex)))
+	v = v >> mutexWaiterShift
+	v = v + (v & mutexLocked)
+	return int(v)
+}
+
+func (m *Mutex) IsLocked() bool {
+	state := atomic.LoadInt32((*int32)(unsafe.Pointer(&m.Mutex)))
+	return state&mutexLocked == mutexLocked
+}
+
+func (m *Mutex) IsWoken() bool {
+	state := atomic.LoadInt32((*int32)(unsafe.Pointer(&m.Mutex)))
+	return state&mutexWoken == mutexWoken
+}
+
+func (m *Mutex) IsStarving() bool {
+	state := atomic.LoadInt32((*int32)(unsafe.Pointer(&m.Mutex)))
+	return state&mutexStarving == mutexStarving
+}

+ 521 - 0
ringbuffer.go

@@ -0,0 +1,521 @@
+package backable_ringbuffer
+
+import (
+	"errors"
+	"unsafe"
+)
+
+var (
+	ErrTooManyDataToWrite = errors.New("too many data to write")
+	ErrIsFull             = errors.New("ringbuffer is full")
+	ErrIsEmpty            = errors.New("ringbuffer is empty")
+	ErrOutOfRange         = errors.New("index out of range")
+	ErrAccuqireLock       = errors.New("no lock to accquire")
+)
+
+type BackableRingBuffer struct {
+	buf    []byte
+	size   int
+	r      int // next position to read
+	w      int // next position to write
+	isFull bool
+	mu     Mutex
+}
+
+func NewBackableRingBuffer(size int) *BackableRingBuffer {
+	b := &BackableRingBuffer{
+		size: size,
+		buf:  make([]byte, size),
+	}
+	return b
+}
+
+func (r *BackableRingBuffer) Read(p []byte) (n int, err error) {
+	if len(p) == 0 {
+		return 0, nil
+	}
+
+	r.mu.Lock()
+	n, err = r.read(p)
+	r.mu.Unlock()
+	return n, err
+}
+
+func (r *BackableRingBuffer) Peep(p []byte) (n int, err error) {
+	if len(p) == 0 {
+		return 0, nil
+	}
+
+	r.mu.Lock()
+	n, err = r.peep(p)
+	r.mu.Unlock()
+	return n, err
+}
+
+func (r *BackableRingBuffer) TryRead(p []byte) (n int, err error) {
+	if len(p) == 0 {
+		return 0, nil
+	}
+
+	ok := r.mu.TryLock()
+	if !ok {
+		return 0, ErrAccuqireLock
+	}
+
+	n, err = r.read(p)
+	r.mu.Unlock()
+	return n, err
+}
+
+func (r *BackableRingBuffer) TryPeep(p []byte) (n int, err error) {
+	if len(p) == 0 {
+		return 0, nil
+	}
+
+	ok := r.mu.TryLock()
+	if !ok {
+		return 0, ErrAccuqireLock
+	}
+
+	n, err = r.peep(p)
+	r.mu.Unlock()
+	return n, err
+}
+
+func (r *BackableRingBuffer) read(p []byte) (n int, err error) {
+	if r.w == r.r && !r.isFull {
+		return 0, ErrIsEmpty
+	}
+
+	if r.w > r.r {
+		n = r.w - r.r
+		if n > len(p) {
+			n = len(p)
+		}
+		copy(p, r.buf[r.r:r.r+n])
+		r.r = (r.r + n) % r.size
+		return
+	}
+
+	n = r.size - r.r + r.w
+	if n > len(p) {
+		n = len(p)
+	}
+
+	if r.r+n <= r.size {
+		copy(p, r.buf[r.r:r.r+n])
+	} else {
+		c1 := r.size - r.r
+		copy(p, r.buf[r.r:r.size])
+		c2 := n - c1
+		copy(p[c1:], r.buf[0:c2])
+	}
+	r.r = (r.r + n) % r.size
+
+	r.isFull = false
+
+	return n, err
+}
+
+func (r *BackableRingBuffer) peep(p []byte) (n int, err error) {
+	if r.w == r.r && !r.isFull {
+		return 0, ErrIsEmpty
+	}
+
+	if r.w > r.r {
+		n = r.w - r.r
+		if n > len(p) {
+			n = len(p)
+		}
+		copy(p, r.buf[r.r:r.r+n])
+		return
+	}
+
+	n = r.size - r.r + r.w
+	if n > len(p) {
+		n = len(p)
+	}
+
+	if r.r+n <= r.size {
+		copy(p, r.buf[r.r:r.r+n])
+	} else {
+		c1 := r.size - r.r
+		copy(p, r.buf[r.r:r.size])
+		c2 := n - c1
+		copy(p[c1:], r.buf[0:c2])
+	}
+	return n, err
+}
+
+func (r *BackableRingBuffer) ReadByte() (b byte, err error) {
+	r.mu.Lock()
+	if r.w == r.r && !r.isFull {
+		r.mu.Unlock()
+		return 0, ErrIsEmpty
+	}
+	b = r.buf[r.r]
+	r.r++
+	if r.r == r.size {
+		r.r = 0
+	}
+
+	r.isFull = false
+	r.mu.Unlock()
+	return b, err
+}
+
+func (r *BackableRingBuffer) PeepByte() (b byte, err error) {
+	r.mu.Lock()
+	if r.w == r.r && !r.isFull {
+		r.mu.Unlock()
+		return 0, ErrIsEmpty
+	}
+	b = r.buf[r.r]
+	r.mu.Unlock()
+	return b, err
+}
+
+func (r *BackableRingBuffer) Write(p []byte) (n int, err error) {
+	if len(p) == 0 {
+		return 0, nil
+	}
+	r.mu.Lock()
+	n, err = r.write(p)
+	r.mu.Unlock()
+
+	return n, err
+}
+
+func (r *BackableRingBuffer) WriteBack(p []byte) (n int, err error) {
+	if len(p) == 0 {
+		return 0, nil
+	}
+	r.mu.Lock()
+	n, err = r.writeBack(p)
+	r.mu.Unlock()
+
+	return n, err
+}
+
+func (r *BackableRingBuffer) TryWrite(p []byte) (n int, err error) {
+	if len(p) == 0 {
+		return 0, nil
+	}
+	ok := r.mu.TryLock()
+	if !ok {
+		return 0, ErrAccuqireLock
+	}
+
+	n, err = r.write(p)
+	r.mu.Unlock()
+
+	return n, err
+}
+
+func (r *BackableRingBuffer) TryWriteBack(p []byte) (n int, err error) {
+	if len(p) == 0 {
+		return 0, nil
+	}
+	ok := r.mu.TryLock()
+	if !ok {
+		return 0, ErrAccuqireLock
+	}
+
+	n, err = r.writeBack(p)
+	r.mu.Unlock()
+
+	return n, err
+}
+
+func (r *BackableRingBuffer) write(p []byte) (n int, err error) {
+	if r.isFull {
+		return 0, ErrIsFull
+	}
+
+	var avail int
+	if r.w >= r.r {
+		avail = r.size - r.w + r.r
+	} else {
+		avail = r.r - r.w
+	}
+
+	if len(p) > avail {
+		err = ErrTooManyDataToWrite
+		p = p[:avail]
+	}
+	n = len(p)
+
+	if r.w >= r.r {
+		c1 := r.size - r.w
+		if c1 >= n {
+			copy(r.buf[r.w:], p)
+			r.w += n
+		} else {
+			copy(r.buf[r.w:], p[:c1])
+			c2 := n - c1
+			copy(r.buf[0:], p[c1:])
+			r.w = c2
+		}
+	} else {
+		copy(r.buf[r.w:], p)
+		r.w += n
+	}
+
+	if r.w == r.size {
+		r.w = 0
+	}
+	if r.w == r.r {
+		r.isFull = true
+	}
+
+	return n, err
+}
+
+func (r *BackableRingBuffer) writeBack(p []byte) (n int, err error) {
+	if r.isFull {
+		return 0, ErrIsFull
+	}
+
+	var avail int
+	if r.w >= r.r {
+		avail = r.size - r.w + r.r
+	} else {
+		avail = r.r - r.w
+	}
+
+	if len(p) > avail {
+		err = ErrTooManyDataToWrite
+		p = p[:avail]
+	}
+	n = len(p)
+
+	if r.w >= r.r {
+		if n <= r.r {
+			stp := r.r - n
+			copy(r.buf[stp:], p)
+			r.r = r.r - n
+		} else {
+			lan := n - r.r
+			stp1 := r.size - lan
+			copy(r.buf[stp1:], p[:lan])
+			copy(r.buf, p[lan:])
+			r.r = stp1
+		}
+	} else {
+		stp2 := r.r - n
+		copy(r.buf[stp2:], p)
+		r.r = stp2
+	}
+
+	if r.w == r.r {
+		r.isFull = true
+	}
+
+	return n, err
+}
+
+func (r *BackableRingBuffer) WriteByte(c byte) error {
+	r.mu.Lock()
+	err := r.writeByte(c)
+	r.mu.Unlock()
+	return err
+}
+
+func (r *BackableRingBuffer) WriteByteBack(c byte) error {
+	r.mu.Lock()
+	err := r.writeByteBack(c)
+	r.mu.Unlock()
+	return err
+}
+
+func (r *BackableRingBuffer) TryWriteByte(c byte) error {
+	ok := r.mu.TryLock()
+	if !ok {
+		return ErrAccuqireLock
+	}
+
+	err := r.writeByte(c)
+	r.mu.Unlock()
+	return err
+}
+
+func (r *BackableRingBuffer) TryWriteByteBack(c byte) error {
+	ok := r.mu.TryLock()
+	if !ok {
+		return ErrAccuqireLock
+	}
+
+	err := r.writeByteBack(c)
+	r.mu.Unlock()
+	return err
+}
+
+func (r *BackableRingBuffer) writeByte(c byte) error {
+	if r.w == r.r && r.isFull {
+		return ErrIsFull
+	}
+	r.buf[r.w] = c
+	r.w++
+
+	if r.w == r.size {
+		r.w = 0
+	}
+	if r.w == r.r {
+		r.isFull = true
+	}
+
+	return nil
+}
+
+func (r *BackableRingBuffer) writeByteBack(c byte) error {
+	if r.w == r.r && r.isFull {
+		return ErrIsFull
+	}
+
+	pos := r.r - 1
+	if pos < 0 {
+		pos = r.size - 1
+	}
+	r.buf[pos] = c
+	r.r = pos
+
+	if r.w == r.r {
+		r.isFull = true
+	}
+
+	return nil
+}
+
+func (r *BackableRingBuffer) Length() int {
+	r.mu.Lock()
+	defer r.mu.Unlock()
+
+	if r.w == r.r {
+		if r.isFull {
+			return r.size
+		}
+		return 0
+	}
+
+	if r.w > r.r {
+		return r.w - r.r
+	}
+
+	return r.size - r.r + r.w
+}
+
+func (r *BackableRingBuffer) Capacity() int {
+	return r.size
+}
+
+func (r *BackableRingBuffer) Free() int {
+	r.mu.Lock()
+	defer r.mu.Unlock()
+
+	if r.w == r.r {
+		if r.isFull {
+			return 0
+		}
+		return r.size
+	}
+
+	if r.w < r.r {
+		return r.r - r.w
+	}
+
+	return r.size - r.w + r.r
+}
+
+func (r *BackableRingBuffer) WriteString(s string) (n int, err error) {
+	x := (*[2]uintptr)(unsafe.Pointer(&s))
+	h := [3]uintptr{x[0], x[1], x[1]}
+	buf := *(*[]byte)(unsafe.Pointer(&h))
+	return r.Write(buf)
+}
+
+func (r *BackableRingBuffer) Bytes() []byte {
+	r.mu.Lock()
+	defer r.mu.Unlock()
+
+	if r.w == r.r {
+		if r.isFull {
+			buf := make([]byte, r.size)
+			copy(buf, r.buf[r.r:])
+			copy(buf[r.size-r.r:], r.buf[:r.w])
+			return buf
+		}
+		return nil
+	}
+
+	if r.w > r.r {
+		buf := make([]byte, r.w-r.r)
+		copy(buf, r.buf[r.r:r.w])
+		return buf
+	}
+
+	n := r.size - r.r + r.w
+	buf := make([]byte, n)
+
+	if r.r+n < r.size {
+		copy(buf, r.buf[r.r:r.r+n])
+	} else {
+		c1 := r.size - r.r
+		copy(buf, r.buf[r.r:r.size])
+		c2 := n - c1
+		copy(buf[c1:], r.buf[0:c2])
+	}
+
+	return buf
+}
+
+func (r *BackableRingBuffer) PeepByteAt(pos int) (b byte, err error) {
+	defer r.mu.Unlock()
+	r.mu.Lock()
+	if r.w == r.r && !r.isFull {
+		return 0, ErrIsEmpty
+	}
+	if r.r == r.w && pos > 0 {
+		return 0, ErrOutOfRange
+	}
+	if r.r < r.w {
+		rpos := r.r + pos
+		b = r.buf[rpos]
+		return
+	} else {
+		if r.r+pos >= r.size {
+			rpos := r.r + pos - r.size
+			if rpos >= r.w {
+				return 0, ErrOutOfRange
+			}
+			b = r.buf[rpos]
+			return
+		} else {
+			rpos := r.r + pos
+			b = r.buf[rpos]
+			return
+		}
+	}
+}
+
+func (r *BackableRingBuffer) IsFull() bool {
+	r.mu.Lock()
+	defer r.mu.Unlock()
+
+	return r.isFull
+}
+
+func (r *BackableRingBuffer) IsEmpty() bool {
+	r.mu.Lock()
+	defer r.mu.Unlock()
+
+	return !r.isFull && r.w == r.r
+}
+
+func (r *BackableRingBuffer) Reset() {
+	r.mu.Lock()
+	defer r.mu.Unlock()
+
+	r.r = 0
+	r.w = 0
+	r.isFull = false
+}