Платформа ЦРНП "Мирокод" для разработки проектов
https://git.mirocod.ru
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
398 lines
10 KiB
398 lines
10 KiB
package gomemcached |
|
|
|
import ( |
|
"encoding/binary" |
|
"fmt" |
|
) |
|
|
|
type FrameObjType int |
|
|
|
const ( |
|
FrameBarrier FrameObjType = iota |
|
FrameDurability FrameObjType = iota |
|
FrameDcpStreamId FrameObjType = iota |
|
FrameOpenTracing FrameObjType = iota |
|
FrameImpersonate FrameObjType = iota |
|
) |
|
|
|
const MAX_USER_LEN = 15 // TODO half byte shifting to be implemented |
|
// it's not very efficient so we currently truncate user names |
|
const FAST_USER_LEN = 15 |
|
|
|
type FrameInfo struct { |
|
ObjId FrameObjType |
|
ObjLen int |
|
ObjData []byte |
|
} |
|
|
|
var ErrorInvalidOp error = fmt.Errorf("Specified method is not applicable") |
|
var ErrorObjLenNotMatch error = fmt.Errorf("Object length does not match data") |
|
|
|
func (f *FrameInfo) Validate() error { |
|
switch f.ObjId { |
|
case FrameBarrier: |
|
if f.ObjLen != 0 { |
|
return fmt.Errorf("Invalid FrameBarrier - length is %v\n", f.ObjLen) |
|
} else if f.ObjLen != len(f.ObjData) { |
|
return ErrorObjLenNotMatch |
|
} |
|
case FrameDurability: |
|
if f.ObjLen != 1 && f.ObjLen != 3 { |
|
return fmt.Errorf("Invalid FrameDurability - length is %v\n", f.ObjLen) |
|
} else if f.ObjLen != len(f.ObjData) { |
|
return ErrorObjLenNotMatch |
|
} |
|
case FrameDcpStreamId: |
|
if f.ObjLen != 2 { |
|
return fmt.Errorf("Invalid FrameDcpStreamId - length is %v\n", f.ObjLen) |
|
} else if f.ObjLen != len(f.ObjData) { |
|
return ErrorObjLenNotMatch |
|
} |
|
case FrameOpenTracing: |
|
if f.ObjLen != 1 { |
|
return fmt.Errorf("Invalid FrameImpersonate - length is %v\n", f.ObjLen) |
|
} else if f.ObjLen != len(f.ObjData) { |
|
return ErrorObjLenNotMatch |
|
} |
|
case FrameImpersonate: |
|
default: |
|
return fmt.Errorf("Unknown FrameInfo type") |
|
} |
|
return nil |
|
} |
|
|
|
func (f *FrameInfo) GetStreamId() (uint16, error) { |
|
if f.ObjId != FrameDcpStreamId { |
|
return 0, ErrorInvalidOp |
|
} |
|
|
|
var output uint16 |
|
output = uint16(f.ObjData[0]) |
|
output = output << 8 |
|
output |= uint16(f.ObjData[1]) |
|
return output, nil |
|
} |
|
|
|
type DurabilityLvl uint8 |
|
|
|
const ( |
|
DuraInvalid DurabilityLvl = iota // Not used (0x0) |
|
DuraMajority DurabilityLvl = iota // (0x01) |
|
DuraMajorityAndPersistOnMaster DurabilityLvl = iota // (0x02) |
|
DuraPersistToMajority DurabilityLvl = iota // (0x03) |
|
) |
|
|
|
func (f *FrameInfo) GetDurabilityRequirements() (lvl DurabilityLvl, timeoutProvided bool, timeoutMs uint16, err error) { |
|
if f.ObjId != FrameDurability { |
|
err = ErrorInvalidOp |
|
return |
|
} |
|
if f.ObjLen != 1 && f.ObjLen != 3 { |
|
err = ErrorObjLenNotMatch |
|
return |
|
} |
|
|
|
lvl = DurabilityLvl(uint8(f.ObjData[0])) |
|
|
|
if f.ObjLen == 3 { |
|
timeoutProvided = true |
|
timeoutMs = binary.BigEndian.Uint16(f.ObjData[1:2]) |
|
} |
|
|
|
return |
|
} |
|
|
|
func incrementMarker(bitsToBeIncremented, byteIncrementCnt *int, framingElen, curObjIdx int) (int, error) { |
|
for *bitsToBeIncremented >= 8 { |
|
*byteIncrementCnt++ |
|
*bitsToBeIncremented -= 8 |
|
} |
|
marker := curObjIdx + *byteIncrementCnt |
|
if marker > framingElen { |
|
return -1, fmt.Errorf("Out of bounds") |
|
} |
|
return marker, nil |
|
} |
|
|
|
func (f *FrameInfo) Bytes() ([]byte, bool) { |
|
return obj2Bytes(f.ObjId, f.ObjLen, f.ObjData) |
|
} |
|
|
|
// TODO implement half byte shifting for impersonate user names |
|
// halfByteRemaining will always be false, because ObjID and Len haven't gotten that large yet |
|
// and user names are truncated |
|
func obj2Bytes(id FrameObjType, len int, data []byte) (output []byte, halfByteRemaining bool) { |
|
if len < 16 { |
|
|
|
// ObjIdentifier - 4 bits + ObjLength - 4 bits |
|
var idAndLen uint8 |
|
idAndLen |= uint8(id) << 4 |
|
idAndLen |= uint8(len) |
|
output = append(output, byte(idAndLen)) |
|
|
|
// Rest is Data |
|
output = append(output, data[:len]...) |
|
|
|
} else { |
|
} |
|
return |
|
} |
|
|
|
func parseFrameInfoObjects(buf []byte, framingElen int) (objs []FrameInfo, err error, halfByteRemaining bool) { |
|
var curObjIdx int |
|
var byteIncrementCnt int |
|
var bitsToBeIncremented int |
|
var marker int |
|
|
|
// Parse frameInfo objects |
|
for curObjIdx = 0; curObjIdx < framingElen; curObjIdx += byteIncrementCnt { |
|
byteIncrementCnt = 0 |
|
var oneFrameObj FrameInfo |
|
|
|
// First get the objId |
|
// ------------------------- |
|
var objId int |
|
var objHeader uint8 = buf[curObjIdx] |
|
var objIdentifierRaw uint8 |
|
if bitsToBeIncremented == 0 { |
|
// ObjHeader |
|
// 0 1 2 3 4 5 6 7 |
|
// ^-----^ |
|
// ObjIdentifierRaw |
|
objIdentifierRaw = (objHeader & 0xf0) >> 4 |
|
} else { |
|
// ObjHeader |
|
// 0 1 2 3 4 5 6 7 |
|
// ^-----^ |
|
// ObjIdentifierRaw |
|
objIdentifierRaw = (objHeader & 0x0f) |
|
} |
|
bitsToBeIncremented += 4 |
|
|
|
marker, err = incrementMarker(&bitsToBeIncremented, &byteIncrementCnt, framingElen, curObjIdx) |
|
if err != nil { |
|
return |
|
} |
|
|
|
// Value is 0-14 |
|
objId = int(objIdentifierRaw & 0xe) |
|
// If bit 15 is set, ID is 15 + value of next byte |
|
if objIdentifierRaw&0x1 > 0 { |
|
if bitsToBeIncremented > 0 { |
|
// ObjHeader |
|
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
// ^-----^ ^---------------^ |
|
// ObjId1 Extension |
|
// ^ marker |
|
buffer := uint16(buf[marker]) |
|
buffer = buffer << 8 |
|
buffer |= uint16(buf[marker+1]) |
|
var extension uint8 = uint8(buffer & 0xff0 >> 4) |
|
objId += int(extension) |
|
} else { |
|
// ObjHeader |
|
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
// ^-----^ ^-------------------^ |
|
// ObjId1 extension |
|
// ^ marker |
|
var extension uint8 = uint8(buf[marker]) |
|
objId += int(extension) |
|
} |
|
bitsToBeIncremented += 8 |
|
} |
|
|
|
marker, err = incrementMarker(&bitsToBeIncremented, &byteIncrementCnt, framingElen, curObjIdx) |
|
if err != nil { |
|
return |
|
} |
|
oneFrameObj.ObjId = FrameObjType(objId) |
|
|
|
// Then get the obj length |
|
// ------------------------- |
|
var objLenRaw uint8 |
|
var objLen int |
|
if bitsToBeIncremented > 0 { |
|
// ObjHeader |
|
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
// ^ ^---------^ |
|
// marker objLen |
|
objLenRaw = uint8(buf[marker]) & 0x0f |
|
} else { |
|
// ObjHeader |
|
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
// ^--------^ |
|
// objLen |
|
// ^ marker |
|
objLenRaw = uint8(buf[marker]) & 0xf0 >> 4 |
|
} |
|
bitsToBeIncremented += 4 |
|
|
|
marker, err = incrementMarker(&bitsToBeIncremented, &byteIncrementCnt, framingElen, curObjIdx) |
|
if err != nil { |
|
return |
|
} |
|
|
|
// Length is 0-14 |
|
objLen = int(objLenRaw & 0xe) |
|
// If bit 15 is set, lenghth is 15 + value of next byte |
|
if objLenRaw&0x1 > 0 { |
|
if bitsToBeIncremented == 0 { |
|
// ObjHeader |
|
// 12 13 14 15 16 17 18 19 20 21 22 23 |
|
// ^---------^ ^--------------------^ |
|
// objLen extension |
|
// ^ marker |
|
var extension uint8 = uint8(buf[marker]) |
|
objLen += int(extension) |
|
} else { |
|
// ObjHeader |
|
// 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
|
// ^--------^ ^---------------------^ |
|
// objLen extension |
|
// ^ marker var buffer uint16 |
|
buffer := uint16(buf[marker]) |
|
buffer = buffer << 8 |
|
buffer |= uint16(buf[marker+1]) |
|
var extension uint8 = uint8(buffer & 0xff0 >> 4) |
|
objLen += int(extension) |
|
} |
|
bitsToBeIncremented += 8 |
|
} |
|
|
|
marker, err = incrementMarker(&bitsToBeIncremented, &byteIncrementCnt, framingElen, curObjIdx) |
|
if err != nil { |
|
return |
|
} |
|
oneFrameObj.ObjLen = objLen |
|
|
|
// The rest is N-bytes of data based on the length |
|
if bitsToBeIncremented == 0 { |
|
// No weird alignment needed |
|
oneFrameObj.ObjData = buf[marker : marker+objLen] |
|
} else { |
|
// 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
|
// ^--------^ ^---------------------^ ^---------> |
|
// objLen extension data |
|
// ^ marker |
|
oneFrameObj.ObjData = ShiftByteSliceLeft4Bits(buf[marker : marker+objLen+1]) |
|
} |
|
err = oneFrameObj.Validate() |
|
if err != nil { |
|
return |
|
} |
|
objs = append(objs, oneFrameObj) |
|
|
|
bitsToBeIncremented += 8 * objLen |
|
marker, err = incrementMarker(&bitsToBeIncremented, &byteIncrementCnt, framingElen, curObjIdx) |
|
} |
|
|
|
if bitsToBeIncremented > 0 { |
|
halfByteRemaining = true |
|
} |
|
return |
|
} |
|
|
|
func ShiftByteSliceLeft4Bits(slice []byte) (replacement []byte) { |
|
var buffer uint16 |
|
var i int |
|
sliceLen := len(slice) |
|
|
|
if sliceLen < 2 { |
|
// Let's not shift less than 16 bits |
|
return |
|
} |
|
|
|
replacement = make([]byte, sliceLen, cap(slice)) |
|
|
|
for i = 0; i < sliceLen-1; i++ { |
|
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
// ^-----^ ^---------------^ ^----------- |
|
// garbage data byte 0 data byte 1 |
|
buffer = uint16(slice[i]) |
|
buffer = buffer << 8 |
|
buffer |= uint16(slice[i+1]) |
|
replacement[i] = uint8(buffer & 0xff0 >> 4) |
|
} |
|
|
|
if i < sliceLen { |
|
lastByte := slice[sliceLen-1] |
|
lastByte = lastByte << 4 |
|
replacement[i] = lastByte |
|
} |
|
return |
|
} |
|
|
|
// The following is used to theoretically support frameInfo ObjID extensions |
|
// for completeness, but they are not very efficient though |
|
func ShiftByteSliceRight4Bits(slice []byte) (replacement []byte) { |
|
var buffer uint16 |
|
var i int |
|
var leftovers uint8 // 4 bits only |
|
var replacementUnit uint16 |
|
var first bool = true |
|
var firstLeftovers uint8 |
|
var lastLeftovers uint8 |
|
sliceLen := len(slice) |
|
|
|
if sliceLen < 2 { |
|
// Let's not shift less than 16 bits |
|
return |
|
} |
|
|
|
if slice[sliceLen-1]&0xf == 0 { |
|
replacement = make([]byte, sliceLen, cap(slice)) |
|
} else { |
|
replacement = make([]byte, sliceLen+1, cap(slice)+1) |
|
} |
|
|
|
for i = 0; i < sliceLen-1; i++ { |
|
buffer = binary.BigEndian.Uint16(slice[i : i+2]) |
|
// (buffer) |
|
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
// ^-------------^ ^-------------------^ |
|
// data byte 0 data byte 1 |
|
// |
|
// into |
|
// |
|
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
// ^-----^ ^---------------^ ^--------------------^ ^----------^ |
|
// zeroes data byte 0 data byte 1 zeroes |
|
|
|
if first { |
|
// The leftover OR'ing will overwrite the first 4 bits of data byte 0. Save them |
|
firstLeftovers = uint8(buffer & 0xf000 >> 12) |
|
first = false |
|
} |
|
replacementUnit = 0 |
|
replacementUnit |= uint16(leftovers) << 12 |
|
replacementUnit |= (buffer & 0xff00) >> 4 // data byte 0 |
|
replacementUnit |= buffer & 0xff >> 4 // data byte 1 first 4 bits |
|
lastLeftovers = uint8(buffer&0xf) << 4 |
|
|
|
replacement[i+1] = byte(replacementUnit) |
|
|
|
leftovers = uint8((buffer & 0x000f) << 4) |
|
} |
|
|
|
replacement[0] = byte(uint8(replacement[0]) | firstLeftovers) |
|
if lastLeftovers > 0 { |
|
replacement[sliceLen] = byte(lastLeftovers) |
|
} |
|
return |
|
} |
|
|
|
func Merge2HalfByteSlices(src1, src2 []byte) (output []byte) { |
|
src1Len := len(src1) |
|
src2Len := len(src2) |
|
output = make([]byte, src1Len+src2Len-1) |
|
|
|
var mergeByte uint8 = src1[src1Len-1] |
|
mergeByte |= uint8(src2[0]) |
|
|
|
copy(output, src1) |
|
copy(output[src1Len:], src2[1:]) |
|
|
|
output[src1Len-1] = byte(mergeByte) |
|
|
|
return |
|
}
|
|
|