mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2024-11-16 15:49:50 +01:00
381 lines
10 KiB
Go
381 lines
10 KiB
Go
package gomemcached
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"fmt"
|
|
)
|
|
|
|
type FrameObjType int
|
|
|
|
const (
|
|
FrameBarrier FrameObjType = iota
|
|
FrameDurability FrameObjType = iota
|
|
FrameDcpStreamId FrameObjType = iota
|
|
FrameOpenTracing FrameObjType = iota
|
|
)
|
|
|
|
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 == 0 {
|
|
return fmt.Errorf("Invalid FrameOpenTracing - length must be > 0")
|
|
} else if f.ObjLen != len(f.ObjData) {
|
|
return ErrorObjLenNotMatch
|
|
}
|
|
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
|
|
}
|
|
|
|
// Right now, halfByteRemaining will always be false, because ObjID and Len haven't gotten that large yet
|
|
func (f *FrameInfo) Bytes() (output []byte, halfByteRemaining bool) {
|
|
// ObjIdentifier - 4 bits + ObjLength - 4 bits
|
|
var idAndLen uint8
|
|
idAndLen |= uint8(f.ObjId) << 4
|
|
idAndLen |= uint8(f.ObjLen)
|
|
output = append(output, byte(idAndLen))
|
|
|
|
// Rest is Data
|
|
output = append(output, f.ObjData...)
|
|
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
|
|
}
|