mirror of https://github.com/certusone/dc4bc.git
a basic implementation of an append-only bulletin board with a file as a storage
This commit is contained in:
parent
25a3450f37
commit
1a7bc55453
|
@ -13,3 +13,5 @@
|
||||||
|
|
||||||
# Dependency directories (remove the comment below to include it)
|
# Dependency directories (remove the comment below to include it)
|
||||||
# vendor/
|
# vendor/
|
||||||
|
|
||||||
|
.idea
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
package storage
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ Storage = (*FileStorage)(nil)
|
||||||
|
|
||||||
|
type FileStorage struct {
|
||||||
|
sync.Mutex
|
||||||
|
file *os.File
|
||||||
|
reader *bufio.Reader
|
||||||
|
}
|
||||||
|
|
||||||
|
func InitFileStorage(filename string) (*FileStorage, error) {
|
||||||
|
var (
|
||||||
|
fs FileStorage
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
if fs.file, err = os.OpenFile(filename, os.O_APPEND|os.O_CREATE|os.O_RDWR, 0644); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
fs.reader = bufio.NewReader(fs.file)
|
||||||
|
return &fs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fs *FileStorage) Post(m Message) error {
|
||||||
|
fs.Lock()
|
||||||
|
defer fs.Unlock()
|
||||||
|
|
||||||
|
data, err := json.Marshal(m)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
data = append(data, '\n')
|
||||||
|
_, err = fs.file.Write(data)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fs *FileStorage) GetMessages(offset int) ([]Message, error) {
|
||||||
|
fs.Lock()
|
||||||
|
defer fs.Unlock()
|
||||||
|
|
||||||
|
var (
|
||||||
|
msgs []Message
|
||||||
|
err error
|
||||||
|
row []byte
|
||||||
|
data Message
|
||||||
|
)
|
||||||
|
if _, err = fs.file.Seek(0, 0); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
row, err = fs.reader.ReadBytes('\n')
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if offset > 0 {
|
||||||
|
offset--
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = json.Unmarshal(row, &data); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
msgs = append(msgs, data)
|
||||||
|
}
|
||||||
|
if err != io.EOF {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return msgs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fs *FileStorage) Close() error {
|
||||||
|
return fs.file.Close()
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
package storage
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/rand"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func randomBytes(n int) []byte {
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
b := make([]byte, n)
|
||||||
|
if _, err := rand.Read(b); err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFull(t *testing.T) {
|
||||||
|
N := 10
|
||||||
|
offset := 5
|
||||||
|
fs, err := InitFileStorage("test")
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
defer fs.Close()
|
||||||
|
msgs := make([]Message, 0, N)
|
||||||
|
for i := 0; i < N; i++ {
|
||||||
|
msg := Message{
|
||||||
|
Data: randomBytes(10),
|
||||||
|
Signature: randomBytes(10),
|
||||||
|
}
|
||||||
|
msgs = append(msgs, msg)
|
||||||
|
if err = fs.Post(msg); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
offsetMsgs, err := fs.GetMessages(offset)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
expectedOffsetMsgs := msgs[offset:]
|
||||||
|
if !reflect.DeepEqual(offsetMsgs, expectedOffsetMsgs) {
|
||||||
|
t.Errorf("expected messages: %v, actual messages: %v", expectedOffsetMsgs, offsetMsgs)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
package storage
|
||||||
|
|
||||||
|
type Message struct {
|
||||||
|
Data []byte `json:"data"`
|
||||||
|
Signature []byte `json:"signature"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Storage interface {
|
||||||
|
Post(message Message) error
|
||||||
|
GetMessages(offset int) ([]Message, error)
|
||||||
|
Close() error
|
||||||
|
}
|
Loading…
Reference in New Issue