commit
4d539bba28
@ -0,0 +1,17 @@
|
|||||||
|
kind: pipeline
|
||||||
|
type: docker
|
||||||
|
name: clone
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Test
|
||||||
|
image: golang:1.18
|
||||||
|
commands:
|
||||||
|
- go env -w GO111MODULE=on
|
||||||
|
- go env -w GOPROXY=https://goproxy.cn,direct
|
||||||
|
- go test -v ./...
|
||||||
|
- name: Benchmark
|
||||||
|
image: golang:1.18
|
||||||
|
commands:
|
||||||
|
- go env -w GO111MODULE=on
|
||||||
|
- go env -w GOPROXY=https://goproxy.cn,direct
|
||||||
|
- go test -bench=. -benchmem
|
@ -0,0 +1,9 @@
|
|||||||
|
.env
|
||||||
|
.git
|
||||||
|
.svn
|
||||||
|
.idea
|
||||||
|
.vscode
|
||||||
|
*.log
|
||||||
|
goinit.sh
|
||||||
|
gomod.sh
|
||||||
|
/vendor/
|
@ -0,0 +1,5 @@
|
|||||||
|
module go.dtapp.net/gophp
|
||||||
|
|
||||||
|
go 1.18
|
||||||
|
|
||||||
|
require go.dtapp.net/gostring v1.0.3
|
@ -0,0 +1,2 @@
|
|||||||
|
go.dtapp.net/gostring v1.0.3 h1:KSOq4D77/g5yZN/bqWfZ0kOOaPr/P1240vg03+XdENI=
|
||||||
|
go.dtapp.net/gostring v1.0.3/go.mod h1:+ggrOvgQDQturi1QGsXEpyRN/ZPoRDaqhMujIk5lrgQ=
|
@ -0,0 +1,116 @@
|
|||||||
|
package gophp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go.dtapp.net/gophp/serialize"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Serialize 序列
|
||||||
|
func Serialize(value any) ([]byte, error) {
|
||||||
|
return serialize.Marshal(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unserialize 反序列
|
||||||
|
func Unserialize(data []byte) (any, error) {
|
||||||
|
return serialize.UnMarshal(data)
|
||||||
|
}
|
||||||
|
func BaseConvert(number string, frombase, tobase int) (string, error) {
|
||||||
|
i, err := strconv.ParseInt(number, frombase, 0)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return strconv.FormatInt(i, tobase), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ArrayColumn array_column()
|
||||||
|
func ArrayColumn(input map[string]map[string]any, columnKey string) []any {
|
||||||
|
columns := make([]any, 0, len(input))
|
||||||
|
for _, val := range input {
|
||||||
|
if v, ok := val[columnKey]; ok {
|
||||||
|
columns = append(columns, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return columns
|
||||||
|
}
|
||||||
|
|
||||||
|
// Strtr strtr()
|
||||||
|
//
|
||||||
|
// If the parameter length is 1, type is: map[string]string
|
||||||
|
// Strtr("baab", map[string]string{"ab": "01"}) will return "ba01"
|
||||||
|
// If the parameter length is 2, type is: string, string
|
||||||
|
// Strtr("baab", "ab", "01") will return "1001", a => 0; b => 1.
|
||||||
|
func Strtr(haystack string, params ...interface{}) string {
|
||||||
|
ac := len(params)
|
||||||
|
if ac == 1 {
|
||||||
|
pairs := params[0].(map[string]string)
|
||||||
|
length := len(pairs)
|
||||||
|
if length == 0 {
|
||||||
|
return haystack
|
||||||
|
}
|
||||||
|
oldnew := make([]string, length*2)
|
||||||
|
for o, n := range pairs {
|
||||||
|
if o == "" {
|
||||||
|
return haystack
|
||||||
|
}
|
||||||
|
oldnew = append(oldnew, o, n)
|
||||||
|
}
|
||||||
|
return strings.NewReplacer(oldnew...).Replace(haystack)
|
||||||
|
} else if ac == 2 {
|
||||||
|
from := params[0].(string)
|
||||||
|
to := params[1].(string)
|
||||||
|
trlen, lt := len(from), len(to)
|
||||||
|
if trlen > lt {
|
||||||
|
trlen = lt
|
||||||
|
}
|
||||||
|
if trlen == 0 {
|
||||||
|
return haystack
|
||||||
|
}
|
||||||
|
str := make([]uint8, len(haystack))
|
||||||
|
var xlat [256]uint8
|
||||||
|
var i int
|
||||||
|
var j uint8
|
||||||
|
if trlen == 1 {
|
||||||
|
for i = 0; i < len(haystack); i++ {
|
||||||
|
if haystack[i] == from[0] {
|
||||||
|
str[i] = to[0]
|
||||||
|
} else {
|
||||||
|
str[i] = haystack[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return string(str)
|
||||||
|
}
|
||||||
|
// trlen != 1
|
||||||
|
for {
|
||||||
|
xlat[j] = j
|
||||||
|
if j++; j == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i = 0; i < trlen; i++ {
|
||||||
|
xlat[from[i]] = to[i]
|
||||||
|
}
|
||||||
|
for i = 0; i < len(haystack); i++ {
|
||||||
|
str[i] = xlat[haystack[i]]
|
||||||
|
}
|
||||||
|
return string(str)
|
||||||
|
}
|
||||||
|
return haystack
|
||||||
|
}
|
||||||
|
func Rtrim(str string, characterMask ...string) string {
|
||||||
|
if len(characterMask) == 0 {
|
||||||
|
return strings.TrimRightFunc(str, unicode.IsSpace)
|
||||||
|
}
|
||||||
|
return strings.TrimRight(str, characterMask[0])
|
||||||
|
}
|
||||||
|
func StrPad(input string, padLength int, padString string) string {
|
||||||
|
output := padString
|
||||||
|
for padLength > len(output) {
|
||||||
|
output += output
|
||||||
|
}
|
||||||
|
if len(input) >= padLength {
|
||||||
|
return input
|
||||||
|
}
|
||||||
|
return output[:padLength-len(input)] + input
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
package serialize
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func numericalValue(value reflect.Value) (float64, bool) {
|
||||||
|
switch value.Type().Kind() {
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
return float64(value.Int()), true
|
||||||
|
|
||||||
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||||
|
return float64(value.Uint()), true
|
||||||
|
|
||||||
|
case reflect.Float32, reflect.Float64:
|
||||||
|
return value.Float(), true
|
||||||
|
|
||||||
|
default:
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func lessValue(a, b reflect.Value) bool {
|
||||||
|
aValue, aNumerical := numericalValue(a)
|
||||||
|
bValue, bNumerical := numericalValue(b)
|
||||||
|
|
||||||
|
if aNumerical && bNumerical {
|
||||||
|
return aValue < bValue
|
||||||
|
}
|
||||||
|
|
||||||
|
if !aNumerical && !bNumerical {
|
||||||
|
// In theory this should mean they are both strings. In reality
|
||||||
|
// they could be any other type and the String() representation
|
||||||
|
// will be something like "<bool>" if it is not a string. Since
|
||||||
|
// distinct values of non-strings still return the same value
|
||||||
|
// here that's what makes the ordering undefined.
|
||||||
|
return strings.Compare(a.String(), b.String()) < 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Numerical values are always treated as less than other types
|
||||||
|
// (including strings that might represent numbers themselves). The
|
||||||
|
// inverse is also true.
|
||||||
|
return aNumerical && !bNumerical
|
||||||
|
}
|
@ -0,0 +1,108 @@
|
|||||||
|
package serialize
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"go.dtapp.net/gostring"
|
||||||
|
"reflect"
|
||||||
|
"sort"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Marshal(value interface{}) ([]byte, error) {
|
||||||
|
if value == nil {
|
||||||
|
return MarshalNil(), nil
|
||||||
|
}
|
||||||
|
t := reflect.TypeOf(value)
|
||||||
|
switch t.Kind() {
|
||||||
|
case reflect.Bool:
|
||||||
|
return MarshalBool(value.(bool)), nil
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
|
||||||
|
reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64,
|
||||||
|
reflect.Float32, reflect.Float64:
|
||||||
|
return MarshalNumber(value), nil
|
||||||
|
case reflect.String:
|
||||||
|
return MarshalString(value.(string)), nil
|
||||||
|
case reflect.Map:
|
||||||
|
return MarshalMap(value)
|
||||||
|
case reflect.Slice:
|
||||||
|
return MarshalSlice(value)
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("marshal: Unknown type %T with value %#v", t, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func MarshalNil() []byte {
|
||||||
|
return []byte("N;")
|
||||||
|
}
|
||||||
|
|
||||||
|
func MarshalBool(value bool) []byte {
|
||||||
|
if value {
|
||||||
|
return []byte("b:1;")
|
||||||
|
}
|
||||||
|
return []byte("b:0;")
|
||||||
|
}
|
||||||
|
|
||||||
|
func MarshalNumber(value interface{}) []byte {
|
||||||
|
var val string
|
||||||
|
isFloat := false
|
||||||
|
switch value.(type) {
|
||||||
|
default:
|
||||||
|
val = "0"
|
||||||
|
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
|
||||||
|
val, _ = gostring.NumericalToString(value)
|
||||||
|
case float32, float64:
|
||||||
|
val, _ = gostring.NumericalToString(value)
|
||||||
|
isFloat = true
|
||||||
|
}
|
||||||
|
if isFloat {
|
||||||
|
return []byte("d:" + val + ";")
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return []byte("i:" + val + ";")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func MarshalString(value string) []byte {
|
||||||
|
return []byte(fmt.Sprintf("s:%d:\"%s\";", len(value), value))
|
||||||
|
}
|
||||||
|
|
||||||
|
func MarshalMap(value interface{}) ([]byte, error) {
|
||||||
|
s := reflect.ValueOf(value)
|
||||||
|
mapKeys := s.MapKeys()
|
||||||
|
sort.Slice(mapKeys, func(i, j int) bool {
|
||||||
|
return lessValue(mapKeys[i], mapKeys[j])
|
||||||
|
})
|
||||||
|
var buffer bytes.Buffer
|
||||||
|
for _, mapKey := range mapKeys {
|
||||||
|
m, err := Marshal(mapKey.Interface())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
buffer.Write(m)
|
||||||
|
|
||||||
|
m, err = Marshal(s.MapIndex(mapKey).Interface())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
buffer.Write(m)
|
||||||
|
}
|
||||||
|
return []byte(fmt.Sprintf("a:%d:{%s}", s.Len(), buffer.String())), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func MarshalSlice(value interface{}) ([]byte, error) {
|
||||||
|
s := reflect.ValueOf(value)
|
||||||
|
var buffer bytes.Buffer
|
||||||
|
for i := 0; i < s.Len(); i++ {
|
||||||
|
m, err := Marshal(i)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
buffer.Write(m)
|
||||||
|
m, err = Marshal(s.Index(i).Interface())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
buffer.Write(m)
|
||||||
|
}
|
||||||
|
return []byte(fmt.Sprintf("a:%d:{%s}", s.Len(), buffer.String())), nil
|
||||||
|
}
|
@ -0,0 +1,227 @@
|
|||||||
|
package serialize
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"go.dtapp.net/gostring"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
const UnserializableObjectMaxLen = int64(10 * 1024 * 1024 * 1024)
|
||||||
|
|
||||||
|
func UnMarshal(data []byte) (interface{}, error) {
|
||||||
|
reader := bytes.NewReader(data)
|
||||||
|
return unMarshalByReader(reader)
|
||||||
|
}
|
||||||
|
|
||||||
|
func unMarshalByReader(reader *bytes.Reader) (interface{}, error) {
|
||||||
|
for {
|
||||||
|
if token, _, err := reader.ReadRune(); err == nil {
|
||||||
|
switch token {
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("UnMarshal: Unknown token %#U", token)
|
||||||
|
case 'N':
|
||||||
|
return unMarshalNil(reader)
|
||||||
|
case 'b':
|
||||||
|
return unMarshalBool(reader)
|
||||||
|
case 'i':
|
||||||
|
return unMarshalNumber(reader, false)
|
||||||
|
case 'd':
|
||||||
|
return unMarshalNumber(reader, true)
|
||||||
|
case 's':
|
||||||
|
return unMarshalString(reader, true)
|
||||||
|
case 'a':
|
||||||
|
return unMarshalArray(reader)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func unMarshalNil(reader *bytes.Reader) (interface{}, error) {
|
||||||
|
err := expect(reader, ';')
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func unMarshalBool(reader *bytes.Reader) (interface{}, error) {
|
||||||
|
var (
|
||||||
|
raw rune
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
err = expect(reader, ':')
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if raw, _, err = reader.ReadRune(); err != nil {
|
||||||
|
return nil, fmt.Errorf("UnMarshal: Error while reading bool value: %v", err)
|
||||||
|
}
|
||||||
|
err = expect(reader, ';')
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return raw == '1', nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func unMarshalNumber(reader *bytes.Reader, isFloat bool) (interface{}, error) {
|
||||||
|
var (
|
||||||
|
raw string
|
||||||
|
err error
|
||||||
|
val interface{}
|
||||||
|
)
|
||||||
|
err = expect(reader, ':')
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if raw, err = readUntil(reader, ';'); err != nil {
|
||||||
|
return nil, fmt.Errorf("UnMarshal: Error while reading number value: %v", err)
|
||||||
|
} else {
|
||||||
|
if isFloat {
|
||||||
|
if val, err = strconv.ParseFloat(raw, 64); err != nil {
|
||||||
|
return nil, fmt.Errorf("UnMarshal: Unable to convert %s to float: %v", raw, err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if val, err = strconv.Atoi(raw); err != nil {
|
||||||
|
return nil, fmt.Errorf("UnMarshal: Unable to convert %s to int: %v", raw, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return val, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func unMarshalString(reader *bytes.Reader, isFinal bool) (interface{}, error) {
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
val interface{}
|
||||||
|
strLen int
|
||||||
|
readLen int
|
||||||
|
)
|
||||||
|
strLen, err = readLength(reader)
|
||||||
|
err = expect(reader, '"')
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if strLen > 0 {
|
||||||
|
buf := make([]byte, strLen, strLen)
|
||||||
|
if readLen, err = reader.Read(buf); err != nil {
|
||||||
|
return nil, fmt.Errorf("UnMarshal: Error while reading string value: %v", err)
|
||||||
|
} else {
|
||||||
|
if readLen != strLen {
|
||||||
|
return nil, fmt.Errorf("UnMarshal: Unable to read string. Expected %d but have got %d bytes", strLen, readLen)
|
||||||
|
} else {
|
||||||
|
val = string(buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = expect(reader, '"')
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if isFinal {
|
||||||
|
err = expect(reader, ';')
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return val, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func unMarshalArray(reader *bytes.Reader) (interface{}, error) {
|
||||||
|
var arrLen int
|
||||||
|
var err error
|
||||||
|
val := make(map[string]interface{})
|
||||||
|
arrLen, err = readLength(reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = expect(reader, '{')
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
indexLen := 0
|
||||||
|
for i := 0; i < arrLen; i++ {
|
||||||
|
k, err := unMarshalByReader(reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
v, err := unMarshalByReader(reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
switch t := k.(type) {
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("UnMarshal: Unexpected key type %T", t)
|
||||||
|
case string:
|
||||||
|
stringKey, _ := k.(string)
|
||||||
|
val[stringKey] = v
|
||||||
|
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64:
|
||||||
|
stringKey, _ := gostring.NumericalToString(k)
|
||||||
|
val[stringKey] = v
|
||||||
|
if i == k {
|
||||||
|
indexLen++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = expect(reader, '}')
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if indexLen == arrLen {
|
||||||
|
var slice []interface{}
|
||||||
|
for _, row := range val {
|
||||||
|
slice = append(slice, row)
|
||||||
|
}
|
||||||
|
return slice, nil
|
||||||
|
}
|
||||||
|
return val, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func expect(reader *bytes.Reader, expected rune) error {
|
||||||
|
if token, _, err := reader.ReadRune(); err != nil {
|
||||||
|
return fmt.Errorf("UnMarshal: Error while reading expected rune %#U: %v", expected, err)
|
||||||
|
} else if token != expected {
|
||||||
|
return fmt.Errorf("UnMarshal: Expected %#U but have got %#U", expected, token)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func readUntil(reader *bytes.Reader, stop rune) (string, error) {
|
||||||
|
var (
|
||||||
|
token rune
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
buf := bytes.NewBuffer([]byte{})
|
||||||
|
for {
|
||||||
|
if token, _, err = reader.ReadRune(); err != nil || token == stop {
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
buf.WriteRune(token)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return buf.String(), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func readLength(reader *bytes.Reader) (int, error) {
|
||||||
|
var (
|
||||||
|
raw string
|
||||||
|
err error
|
||||||
|
val int
|
||||||
|
)
|
||||||
|
err = expect(reader, ':')
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if raw, err = readUntil(reader, ':'); err != nil {
|
||||||
|
return 0, fmt.Errorf("UnMarshal: Error while reading length of value: %v", err)
|
||||||
|
} else {
|
||||||
|
if val, err = strconv.Atoi(raw); err != nil {
|
||||||
|
return 0, fmt.Errorf("UnMarshal: Unable to convert %s to int: %v", raw, err)
|
||||||
|
} else if int64(val) > UnserializableObjectMaxLen {
|
||||||
|
return 0, fmt.Errorf("UnMarshal: Unserializable object length looks too big(%d). If you are sure you wanna unserialise it, please increase UNSERIALIZABLE_OBJECT_MAX_LEN const", val)
|
||||||
|
val = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return val, nil
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
package gophp
|
||||||
|
|
||||||
|
const Version = "1.0.0"
|
@ -0,0 +1,7 @@
|
|||||||
|
package gophp
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestVersion(t *testing.T) {
|
||||||
|
t.Log(Version)
|
||||||
|
}
|
Loading…
Reference in new issue