github.com/trim21/go-phpserialize@v0.0.22-0.20240301204449-2fca0319b3f0/internal/decoder/context.go (about)

     1  package decoder
     2  
     3  import (
     4  	"strconv"
     5  	"sync"
     6  	"unsafe"
     7  
     8  	"github.com/trim21/go-phpserialize/internal/errors"
     9  )
    10  
    11  type RuntimeContext struct {
    12  	Buf []byte
    13  }
    14  
    15  var (
    16  	runtimeContextPool = sync.Pool{
    17  		New: func() any {
    18  			return &RuntimeContext{}
    19  		},
    20  	}
    21  )
    22  
    23  func TakeRuntimeContext() *RuntimeContext {
    24  	return runtimeContextPool.Get().(*RuntimeContext)
    25  }
    26  
    27  func ReleaseRuntimeContext(ctx *RuntimeContext) {
    28  	runtimeContextPool.Put(ctx)
    29  }
    30  
    31  var (
    32  	isWhiteSpace = [256]bool{}
    33  
    34  	isInteger = [256]bool{}
    35  )
    36  
    37  func init() {
    38  	isWhiteSpace[' '] = true
    39  	isWhiteSpace['\n'] = true
    40  	isWhiteSpace['\t'] = true
    41  	isWhiteSpace['\r'] = true
    42  
    43  	for i := 0; i < 10; i++ {
    44  		isInteger[[]byte(strconv.Itoa(i))[0]] = true
    45  	}
    46  }
    47  
    48  func char(ptr unsafe.Pointer, offset int64) byte {
    49  	return *(*byte)(unsafe.Pointer(uintptr(ptr) + uintptr(offset)))
    50  }
    51  
    52  // `:${length}:`
    53  func skipLengthWithBothColon(buf []byte, cursor int64) (int64, error) {
    54  	if buf[cursor] != ':' {
    55  		return cursor, errors.ErrExpected("':' before length", cursor)
    56  	}
    57  	cursor++
    58  
    59  	for isInteger[buf[cursor]] {
    60  		cursor++
    61  	}
    62  
    63  	if buf[cursor] != ':' {
    64  		return cursor, errors.ErrExpected("':' after length", cursor)
    65  	}
    66  
    67  	cursor++
    68  
    69  	return cursor, nil
    70  }
    71  
    72  // jump from 'O' to colon ':' before array length
    73  func skipClassName(buf []byte, cursor int64) (int64, error) {
    74  	// O:8:"stdClass":1:{s:1:"a";s:1:"q";}
    75  	end, err := skipString(buf, cursor)
    76  	return end - 2, err
    77  }
    78  
    79  func skipString(buf []byte, cursor int64) (int64, error) {
    80  	cursor++
    81  	sLen, end, err := readLength(buf, cursor)
    82  	if err != nil {
    83  		return cursor, err
    84  	}
    85  
    86  	return end + sLen + 3, nil
    87  }
    88  
    89  func skipArray(buf []byte, cursor, depth int64) (int64, error) {
    90  	bracketCount := 1
    91  	end, err := skipLengthWithBothColon(buf, cursor)
    92  	if err != nil {
    93  		return cursor, err
    94  	}
    95  	cursor = end
    96  
    97  	for {
    98  		switch buf[cursor] {
    99  		// '{' and '}' may only appear in array or string,
   100  		// we will skip value content, it's easy to only scan char '{' and '}'
   101  		case 's':
   102  			end, err := skipString(buf, cursor)
   103  			if err != nil {
   104  				return cursor, err
   105  			}
   106  			cursor = end
   107  		case '}':
   108  			bracketCount--
   109  			depth--
   110  			if bracketCount == 0 {
   111  				return cursor + 1, nil
   112  			}
   113  		case '{':
   114  			depth++
   115  			if depth > maxDecodeNestingDepth {
   116  				return 0, errors.ErrExceededMaxDepth(buf[cursor], cursor)
   117  			}
   118  			cursor++
   119  		default:
   120  			cursor++
   121  		}
   122  	}
   123  }
   124  
   125  func skipValue(buf []byte, cursor, depth int64) (int64, error) {
   126  	switch buf[cursor] {
   127  	case 'O':
   128  		end, err := skipClassName(buf, cursor)
   129  		if err != nil {
   130  			return cursor, err
   131  		}
   132  		cursor = end
   133  		fallthrough
   134  	case 'a':
   135  		return skipArray(buf, cursor+1, depth+1)
   136  	case 's':
   137  		return skipString(buf, cursor)
   138  	// case 'd':
   139  	case 'i':
   140  		cursor++
   141  		end, err := skipLengthWithBothColon(buf, cursor)
   142  		if err != nil {
   143  			return cursor, err
   144  		}
   145  		return end + 1, nil
   146  	case 'b':
   147  		cursor++
   148  		if buf[cursor] != ':' {
   149  			return 0, errors.ErrUnexpectedEnd("':' before bool value", cursor)
   150  		}
   151  		cursor++
   152  		switch buf[cursor] {
   153  		case '0':
   154  		case '1':
   155  		default:
   156  			return 0, errors.ErrUnexpectedEnd("'0' pr '1' af bool value", cursor)
   157  		}
   158  		cursor++
   159  		if buf[cursor] != ';' {
   160  			return 0, errors.ErrUnexpectedEnd("';' end bool value", cursor)
   161  		}
   162  		cursor++
   163  		return cursor, nil
   164  
   165  	case 'N':
   166  		if err := validateNull(buf, cursor); err != nil {
   167  			return 0, err
   168  		}
   169  		cursor += 2
   170  		return cursor, nil
   171  	default:
   172  		return cursor, errors.ErrUnexpectedEnd("null", cursor)
   173  	}
   174  
   175  }
   176  
   177  // caller should check `a:0` , this function check  `0:{};`
   178  func validateEmptyArray(buf []byte, cursor int64) error {
   179  	if cursor+4 > int64(len(buf)) {
   180  		return errors.ErrUnexpectedEnd("array", cursor)
   181  	}
   182  
   183  	if buf[cursor+1] != ':' {
   184  		return errors.ErrExpected("':' before array length", cursor+1)
   185  	}
   186  	if buf[cursor+2] != '{' {
   187  		return errors.ErrInvalidBeginningOfArray(buf[cursor+2], cursor+2)
   188  	}
   189  	if buf[cursor+3] != '}' {
   190  		return errors.ErrExpected("empty array end with '}'", cursor+3)
   191  
   192  	}
   193  
   194  	return nil
   195  }
   196  
   197  func validateNull(buf []byte, cursor int64) error {
   198  	if cursor+1 >= int64(len(buf)) {
   199  		return errors.ErrUnexpectedEnd("null", cursor)
   200  	}
   201  	if buf[cursor+1] != ';' {
   202  		return errors.ErrInvalidCharacter(buf[cursor+1], "null", cursor)
   203  	}
   204  	return nil
   205  }
   206  
   207  // :${length}:
   208  func readLength(buf []byte, cursor int64) (int64, int64, error) {
   209  	end, err := skipLengthWithBothColon(buf, cursor)
   210  	if err != nil {
   211  		return 0, cursor, err
   212  	}
   213  
   214  	return parseByteStringInt64(buf[cursor+1 : end-1]), end, nil
   215  }
   216  
   217  // :${length}:
   218  func readLengthInt(buf []byte, cursor int64) (int, int64, error) {
   219  	end, err := skipLengthWithBothColon(buf, cursor)
   220  	if err != nil {
   221  		return 0, cursor, err
   222  	}
   223  
   224  	return parseByteStringInt(buf[cursor+1 : end-1]), end, nil
   225  }
   226  
   227  // `:${length}:"${content}";`
   228  func readString(buf []byte, cursor int64) ([]byte, int64, error) {
   229  	sLen, end, err := readLength(buf, cursor)
   230  	if err != nil {
   231  		return nil, 0, err
   232  	}
   233  
   234  	start := end + 1
   235  	end = end + sLen + 1
   236  
   237  	if buf[end] != '"' {
   238  		return nil, end, errors.ErrExpected(`string quoted '"'`, end)
   239  	}
   240  	cursor = end + 1
   241  	if buf[cursor] != ';' {
   242  		return nil, end, errors.ErrExpected(`string end ';'`, cursor)
   243  	}
   244  
   245  	cursor++
   246  
   247  	return buf[start:end], cursor, nil
   248  }
   249  
   250  func parseByteStringInt64(b []byte) int64 {
   251  	var l int64
   252  	for _, c := range b {
   253  		l = l*10 + int64(c-'0')
   254  	}
   255  
   256  	return l
   257  }
   258  
   259  func parseByteStringInt(b []byte) int {
   260  	var l int
   261  	for _, c := range b {
   262  		l = l*10 + int(c-'0')
   263  	}
   264  
   265  	return l
   266  }
   267  
   268  func readBool(buf []byte, cursor int64) (bool, error) {
   269  	if cursor+1 >= int64(len(buf)) {
   270  		return false, errors.ErrUnexpectedEnd("null", cursor)
   271  	}
   272  
   273  	if buf[cursor+1] != ':' {
   274  		return false, errors.ErrInvalidCharacter(buf[cursor+1], "bool", cursor)
   275  	}
   276  
   277  	var v bool
   278  	switch buf[cursor+2] {
   279  	case '1':
   280  		v = true
   281  	case '0':
   282  		v = false
   283  	default:
   284  		return false, errors.ErrInvalidCharacter(buf[cursor+2], "bool", cursor)
   285  	}
   286  
   287  	if buf[cursor+3] != ';' {
   288  		return false, errors.ErrInvalidCharacter(buf[cursor+3], "bool", cursor+3)
   289  	}
   290  
   291  	return v, nil
   292  }