73 lines
1.6 KiB
Go
73 lines
1.6 KiB
Go
package models
|
|
|
|
import "6.5840/porcupine"
|
|
import "fmt"
|
|
import "sort"
|
|
|
|
type KvInput struct {
|
|
Op uint8 // 0 => get, 1 => put, 2 => append
|
|
Key string
|
|
Value string
|
|
}
|
|
|
|
type KvOutput struct {
|
|
Value string
|
|
}
|
|
|
|
var KvModel = porcupine.Model{
|
|
Partition: func(history []porcupine.Operation) [][]porcupine.Operation {
|
|
m := make(map[string][]porcupine.Operation)
|
|
for _, v := range history {
|
|
key := v.Input.(KvInput).Key
|
|
m[key] = append(m[key], v)
|
|
}
|
|
keys := make([]string, 0, len(m))
|
|
for k := range m {
|
|
keys = append(keys, k)
|
|
}
|
|
sort.Strings(keys)
|
|
ret := make([][]porcupine.Operation, 0, len(keys))
|
|
for _, k := range keys {
|
|
ret = append(ret, m[k])
|
|
}
|
|
return ret
|
|
},
|
|
Init: func() interface{} {
|
|
// note: we are modeling a single key's value here;
|
|
// we're partitioning by key, so this is okay
|
|
return ""
|
|
},
|
|
Step: func(state, input, output interface{}) (bool, interface{}) {
|
|
inp := input.(KvInput)
|
|
out := output.(KvOutput)
|
|
st := state.(string)
|
|
if inp.Op == 0 {
|
|
// get
|
|
return out.Value == st, state
|
|
} else if inp.Op == 1 {
|
|
// put
|
|
return true, inp.Value
|
|
} else if inp.Op == 2 {
|
|
// append
|
|
return true, (st + inp.Value)
|
|
} else {
|
|
// append with return value
|
|
return out.Value == st, (st + inp.Value)
|
|
}
|
|
},
|
|
DescribeOperation: func(input, output interface{}) string {
|
|
inp := input.(KvInput)
|
|
out := output.(KvOutput)
|
|
switch inp.Op {
|
|
case 0:
|
|
return fmt.Sprintf("get('%s') -> '%s'", inp.Key, out.Value)
|
|
case 1:
|
|
return fmt.Sprintf("put('%s', '%s')", inp.Key, inp.Value)
|
|
case 2:
|
|
return fmt.Sprintf("append('%s', '%s')", inp.Key, inp.Value)
|
|
default:
|
|
return "<invalid>"
|
|
}
|
|
},
|
|
}
|