First version
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..c56069f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+*.test
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..f7b469a
--- /dev/null
+++ b/README.md
@@ -0,0 +1,59 @@
+# buffruneio
+
+Buffruneio is a wrapper around bufio to provide buffered runes access with
+unlimited unreads.
+
+```go
+import "github.com/pelletier/go-buffruneio"
+```
+
+## Examples
+
+```go
+import (
+    "fmt"
+    "github.com/pelletier/go-buffruneio"
+    "strings"
+)
+
+reader := buffruneio.NewReader(strings.NewReader("abcd"))
+fmt.Println(reader.ReadRune()) // 'a'
+fmt.Println(reader.ReadRune()) // 'b'
+fmt.Println(reader.ReadRune()) // 'c'
+reader.UnreadRune()
+reader.UnreadRune()
+fmt.Println(reader.ReadRune()) // 'b'
+fmt.Println(reader.ReadRune()) // 'c'
+```
+
+## Documentation
+
+The documentation and additional examples are available at
+[godoc.org](http://godoc.org/github.com/pelletier/go-buffruneio).
+
+## Contribute
+
+Feel free to report bugs and patches using GitHub's pull requests system on
+[pelletier/go-toml](https://github.com/pelletier/go-buffruneio). Any feedback is
+much appreciated!
+
+## LICENSE
+
+Copyright (c) 2016 Thomas Pelletier
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/buffruneio.go b/buffruneio.go
new file mode 100644
index 0000000..9e47ca2
--- /dev/null
+++ b/buffruneio.go
@@ -0,0 +1,85 @@
+// Package buffruneio is a wrapper around bufio to provide buffered runes access with unlimited unreads.
+package buffruneio
+
+import (
+	"bufio"
+	"container/list"
+	"errors"
+	"io"
+)
+
+// Rune to indicate end of file.
+const (
+	EOF = -(iota + 1)
+)
+
+// ErrNoRuneToUnread is returned by UnreadRune() when the read index is already at the beginning of the buffer.
+var ErrNoRuneToUnread = errors.New("no rune to unwind")
+
+// Reader implements runes buffering for an io.Reader object.
+type Reader struct {
+	buffer  *list.List
+	current *list.Element
+	input   *bufio.Reader
+}
+
+// NewReader returns a new Reader.
+func NewReader(rd io.Reader) *Reader {
+	return &Reader{
+		buffer: list.New(),
+		input:  bufio.NewReader(rd),
+	}
+}
+
+func (rd *Reader) feedBuffer() error {
+	r, _, err := rd.input.ReadRune()
+
+	if err != nil {
+		if err != io.EOF {
+			return err
+		}
+		r = EOF
+	}
+
+	rd.buffer.PushBack(r)
+	if rd.current == nil {
+		rd.current = rd.buffer.Back()
+	}
+	return nil
+}
+
+// ReadRune reads the next rune from buffer, or from the underlying reader if needed.
+func (rd *Reader) ReadRune() (rune, error) {
+	if rd.current == rd.buffer.Back() || rd.current == nil {
+		err := rd.feedBuffer()
+		if err != nil {
+			return EOF, err
+		}
+	}
+
+	r := rd.current.Value
+	rd.current = rd.current.Next()
+	return r.(rune), nil
+}
+
+// UnreadRune pushes back the previously read rune in the buffer, extending it if needed.
+func (rd *Reader) UnreadRune() error {
+	if rd.current == rd.buffer.Front() {
+		return ErrNoRuneToUnread
+	}
+	if rd.current == nil {
+		rd.current = rd.buffer.Back()
+	} else {
+		rd.current = rd.current.Prev()
+	}
+	return nil
+}
+
+// Forget removes runes stored before the current stream position index.
+func (rd *Reader) Forget() {
+	if rd.current == nil {
+		rd.current = rd.buffer.Back()
+	}
+	for ; rd.current != rd.buffer.Front(); rd.buffer.Remove(rd.current.Prev()) {
+	}
+}
diff --git a/buffruneio_test.go b/buffruneio_test.go
new file mode 100644
index 0000000..95946b6
--- /dev/null
+++ b/buffruneio_test.go
@@ -0,0 +1,84 @@
+package buffruneio
+
+import (
+	"strings"
+	"testing"
+)
+
+func assumeRune(t *testing.T, rd *Reader, r rune) {
+	gotRune, err := rd.ReadRune()
+	if err != nil {
+		t.Fatal("unexpected error", err)
+	}
+	if gotRune != r {
+		t.Fatal("got", string(gotRune),
+			"(", []byte(string(gotRune)), ")",
+			"expected", string(r),
+			"(", []byte(string(r)), ")")
+	}
+}
+
+func TestReadString(t *testing.T) {
+	s := "hello"
+	rd := NewReader(strings.NewReader(s))
+
+	assumeRune(t, rd, 'h')
+	assumeRune(t, rd, 'e')
+	assumeRune(t, rd, 'l')
+	assumeRune(t, rd, 'l')
+	assumeRune(t, rd, 'o')
+	assumeRune(t, rd, EOF)
+}
+
+func TestMultipleEOF(t *testing.T) {
+	s := ""
+	rd := NewReader(strings.NewReader(s))
+
+	assumeRune(t, rd, EOF)
+	assumeRune(t, rd, EOF)
+}
+
+func TestUnread(t *testing.T) {
+	s := "ab"
+	rd := NewReader(strings.NewReader(s))
+
+	assumeRune(t, rd, 'a')
+	assumeRune(t, rd, 'b')
+	rd.UnreadRune()
+	assumeRune(t, rd, 'b')
+	assumeRune(t, rd, EOF)
+}
+
+func TestUnreadEOF(t *testing.T) {
+	s := ""
+	rd := NewReader(strings.NewReader(s))
+
+	rd.UnreadRune()
+	assumeRune(t, rd, EOF)
+	assumeRune(t, rd, EOF)
+	rd.UnreadRune()
+	assumeRune(t, rd, EOF)
+}
+
+func TestForget(t *testing.T) {
+	s := "hello"
+	rd := NewReader(strings.NewReader(s))
+
+	assumeRune(t, rd, 'h')
+	assumeRune(t, rd, 'e')
+	assumeRune(t, rd, 'l')
+	assumeRune(t, rd, 'l')
+	rd.Forget()
+	if rd.UnreadRune() != ErrNoRuneToUnread {
+		t.Fatal("no rune should be available")
+	}
+}
+
+func TestForgetEmpty(t *testing.T) {
+	s := ""
+	rd := NewReader(strings.NewReader(s))
+
+	rd.Forget()
+	assumeRune(t, rd, EOF)
+	rd.Forget()
+}