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)
|
||||
# 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