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 }