Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
rust/target
.idea
File renamed without changes.
5 changes: 5 additions & 0 deletions go/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module vm

go 1.13

require github.com/cespare/xxhash v1.1.0
3 changes: 3 additions & 0 deletions go/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
159 changes: 159 additions & 0 deletions go/oracle_cache.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
package _go

import (
"encoding/binary"
"fmt"
"github.com/cespare/xxhash"
"strings"
"vm/shm"
)

const LenSize uint32 = 4
const ItemSize uint32 = 16
const TickerSize uint32 = 8

type OracleCache struct {
mem *shm.Memory
capacity uint32
}

func CreateOracleCache(name string, capacity uint32) (*OracleCache, error) {
bufferSize := int32(LenSize + (capacity * ItemSize))
mem, err := shm.Open(name, bufferSize)
if err != nil {
mem, err = shm.Create(name, bufferSize)
if err != nil {
return nil, err
}
}

return &OracleCache{mem, capacity}, nil
}

func (o OracleCache) Clear() {
o.setSize(0)
}

func (o OracleCache) PutPrice(ticker string, price uint64) bool {
hash := xxTicker(ticker)
priceBytes := uint64ToBytes(price)
first, last := o.findIndex(hash)
if first == last {
o.storeAtIndex(first, uint64ToBytes(hash), priceBytes)
return true
} else {
if o.Len() == o.capacity {
return false
} else {
o.setSize(o.Len() + 1)
o.shiftRight(first)
o.storeAtIndex(first, uint64ToBytes(hash), priceBytes)
return true
}
}
}

func (o OracleCache) GetPrice(ticker string) *uint64 {
hash := xxTicker(ticker)
first, last := o.findIndex(hash)
if first == last {
price := bytesToUint64(o.getByIndex(first)[TickerSize:])
return &price
} else {
return nil
}
}

func (o OracleCache) Len() uint32 {
bs := make([]byte, 4)
o.mem.ReadAt(bs, 0)
return binary.LittleEndian.Uint32(bs)
}

func (o OracleCache) Close() (err error) {
return o.mem.Close()
}

func (o OracleCache) ToString() string {
buff := "["
l := o.Len()
for i := 0; i < int(l); i++ {
value := o.getByIndex(uint32(i))
ticker := bytesToUint64(value[0 : TickerSize+1])
price := bytesToUint64(value[TickerSize:])
buff += fmt.Sprintf("%d -> %d, ", ticker, price)
}
buff += "]"
return buff
}

func (o OracleCache) findIndex(ticker uint64) (uint32, uint32) {
len := o.Len()
if len == 0 {
return 0, 1
}

first := uint32(0)
last := len
for {
middle := (first + last) / 2
middleTicker := bytesToUint64(o.getByIndex(middle)[0:TickerSize])
if ticker == middleTicker {
return middle, middle
} else if ticker < middleTicker {
last = middle
} else {
first = middle + 1
}

if first >= last {
last += 1
break
}
}

return first, last
}

func offset(index uint32) uint32 {
return ItemSize*index + LenSize
}

func (o OracleCache) storeAtIndex(index uint32, ticker []byte, price []byte) {
offset := offset(index)
o.mem.WriteAt(ticker, int64(offset))
o.mem.WriteAt(price, int64(offset+TickerSize))
}

func (o OracleCache) getByIndex(index uint32) []byte {
offset := offset(index)
return o.mem.Slice(int64(offset), int64(ItemSize+offset))
}

func (o OracleCache) setSize(size uint32) {
bs := make([]byte, 4)
binary.LittleEndian.PutUint32(bs, size)
o.mem.WriteAt(bs, 0)
}

func (o OracleCache) shiftRight(index uint32) {
startOffset := offset(index)
endOffset := offset(o.Len() - 1)
bs := make([]byte, endOffset-startOffset)
o.mem.ReadAt(bs, int64(startOffset))
o.mem.WriteAt(bs, int64(offset(index+1)))
}

func xxTicker(ticker string) uint64 {
return xxhash.Sum64String(strings.ToLower(ticker))
}

func uint64ToBytes(int uint64) []byte {
bs := make([]byte, 8)
binary.LittleEndian.PutUint64(bs, int)
return bs
}

func bytesToUint64(price []byte) uint64 {
return binary.LittleEndian.Uint64(price)
}
64 changes: 64 additions & 0 deletions go/oracle_cache_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package _go

import (
"fmt"
"testing"
)

func TestOracleCache(t *testing.T) {
cache, err := CreateOracleCache("/test", 100)
if err != nil {
t.Errorf("Failed to create cache")
}
defer cache.Close()
cache.Clear()

cache.PutPrice("BTCUSD", 8000)
cache.PutPrice("USDRUB", 70)

if *cache.GetPrice("USDRUB") != 70 {
t.Errorf("Invalid cache data")
}

if *cache.GetPrice("BTCuSD") != 8000 {
t.Errorf("Invalid cache data")
}

if cache.GetPrice("CuSD") != nil {
t.Errorf("Invalid cache data")
}

cache.PutPrice("USDRUB", 80)
if *cache.GetPrice("USDRUB") != 80 {
t.Errorf("Invalid cache data")
}
}

func TestCreateOracleCapacity(t *testing.T) {
cache, err := CreateOracleCache("/test_1", 100)
if err != nil {
t.Errorf("Failed to create cache")
}
defer cache.Close()
cache.Clear()

for i := 0; i < 100; i++ {
if !cache.PutPrice(fmt.Sprintf("T:%d", i), uint64(i)) {
t.Errorf("Failed to put oracle value")
}
}

if cache.Len() != 100 {
t.Errorf("Invalid cache len")
}

if cache.PutPrice("T:101", 101) {
t.Errorf("Cache overflow")
}

for i := 0; i < 100; i++ {
if *cache.GetPrice(fmt.Sprintf("T:%d", i)) != uint64(i) {
t.Errorf("Invalid cache value")
}
}
}
21 changes: 21 additions & 0 deletions go/shm/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2017 hidez8891

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.
49 changes: 49 additions & 0 deletions go/shm/shm.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package shm

// Memory is shared memory struct
type Memory struct {
m *shmi
}

// Create is create shared memory
func Create(name string, size int32) (*Memory, error) {
m, err := create(name, size)
if err != nil {
return nil, err
}
return &Memory{m}, nil
}

// Open is open exist shared memory
func Open(name string, size int32) (*Memory, error) {
m, err := open(name, size)
if err != nil {
return nil, err
}
return &Memory{m}, nil
}

// Close is close & discard shared memory
func (o *Memory) Close() (err error) {
if o.m != nil {
err = o.m.close()
if err == nil {
o.m = nil
}
}
return err
}

func (o *Memory) Slice(off int64, size int64) []byte {
return o.m.memRef(off, size)
}

// ReadAt is read shared memory (offset)
func (o *Memory) ReadAt(p []byte, off int64) int {
return o.m.readAt(p, off)
}

// WriteAt is write shared memory (offset)
func (o *Memory) WriteAt(p []byte, off int64) int {
return o.m.writeAt(p, off)
}
Loading