- init
continuous-integration/drone/push Build is passing Details

master v1.0.0
李光春 2 years ago
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

9
.gitignore vendored

@ -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,78 @@
package gophp
import (
"encoding/json"
"fmt"
"testing"
)
func TestSerialize(t *testing.T) {
t.Log(Unserialize([]byte(`a:1:{s:4:"test";i:34343;}`)))
serialize, _ := Serialize(map[string]interface{}{
"test": 34343,
})
t.Log(string(serialize))
}
func BenchmarkSerialize(b *testing.B) {
for i := 0; i < b.N; i++ {
serialize, _ := Serialize(map[string]interface{}{
"test": 34343,
})
b.Log(string(serialize))
}
}
func TestUnserialize(t *testing.T) {
t.Log(Unserialize([]byte(`a:1:{s:4:"test";i:34343;}`)))
}
func BenchmarkUnserialize(b *testing.B) {
for i := 0; i < b.N; i++ {
b.Log(Unserialize([]byte(`a:1:{s:4:"test";i:34343;}`)))
}
}
type student struct {
Amount string `json:"amount"`
CreateTime string `json:"create_time"`
UpdateTime string `json:"update_time"`
UserID string `json:"user_id"`
}
func TestPhp2(t *testing.T) {
stu := student{
Amount: "1",
}
m := make(map[string]interface{})
// struct 转json
j, _ := json.Marshal(stu)
// json 转map
json.Unmarshal(j, &m)
fmt.Printf("Serialize %s\n", j)
serialize, err := Serialize(j)
fmt.Printf("Serialize %s\n", serialize)
fmt.Printf("Serialize %s\n", err)
}
func TestUnserialize2(t *testing.T) {
unserialize, _ := Unserialize([]byte(`a:2:{i:0;a:4:{s:7:"user_id";s:5:"10118";s:6:"amount";s:5:"69.00";s:11:"create_time";s:19:"2021-01-04 16:29:03";s:11:"update_time";s:19:"2021-06-15 16:02:46";}i:1;a:4:{s:7:"user_id";s:5:"10088";s:6:"amount";s:5:"10.00";s:11:"create_time";s:19:"2021-01-04 15:46:10";s:11:"update_time";s:19:"2021-06-15 15:50:06";}}`))
fmt.Printf("%s\n", unserialize)
arr, _ := json.Marshal(unserialize)
fmt.Printf("arr%s\n", arr)
var stu []student
_ = json.Unmarshal(arr, &stu)
fmt.Printf("stu%v\n", stu)
}
func TestUnserialize3(t *testing.T) {
unserialize, err := Unserialize([]byte(`a:3:{s:4:"self";a:5:{s:2:"id";i:32;s:7:"user_id";i:10118;s:16:"merchant_user_id";i:10150;s:5:"level";i:3;s:5:"split";d:2.825;}s:5:"split";a:2:{i:0;a:7:{s:2:"id";i:12;s:7:"user_id";i:10000;s:13:"agent_user_id";i:10088;s:16:"user_golden_bean";d:5;s:5:"level";i:1;s:11:"calculation";d:0.010000000000000002;s:5:"split";d:0.57;}i:1;a:7:{s:2:"id";i:19;s:7:"user_id";i:10088;s:13:"agent_user_id";i:10118;s:16:"user_golden_bean";d:4;s:5:"level";i:2;s:11:"calculation";d:0.04;s:5:"split";d:2.26;}}s:11:"golden_bean";a:6:{s:4:"fans";d:56.5;s:12:"fan_referrer";d:16.95;s:8:"merchant";d:4.5200000000000005;s:5:"agent";d:2.825;s:5:"count";d:80.795;s:2:"bf";a:9:{s:2:"id";i:2;s:7:"user_id";i:10088;s:4:"fans";d:50;s:12:"fan_referrer";d:30;s:8:"merchant";d:8;s:6:"system";d:0;s:5:"agent";d:5;s:11:"create_time";s:19:"2020-11-20 14:47:21";s:11:"update_time";s:19:"2020-11-20 14:47:21";}}}`))
fmt.Printf("%+v\n", unserialize)
if err != nil {
fmt.Printf("err%v\n", err)
return
}
}

@ -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…
Cancel
Save