You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

332 lines
6.9 KiB

// ------------------------------------------------------------------------
// Project atila
// Active Thing (activething.com) git.activething.com/go
//
// File name packer.go
// Created by DEV
// Modified 23/01/2024
//
// Copyright 2024 activething.com
// ------------------------------------------------------------------------
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ------------------------------------------------------------------------
package pjson
import (
"activething.com/go/gogo/core/bins"
"activething.com/go/gogo/core/errs"
"activething.com/go/gogo/packets"
"encoding/base64"
"math"
"strconv"
"time"
"unicode/utf8"
)
type (
Packer struct {
options Options
state *packets.State
}
)
func (p *Packer) Pack (value any) {
if !p.state.SetValue() {
panic(p.state.Error)
}
}
func (p *Packer) PackRaw (value []byte) {
if !p.state.SetValue() {
panic(p.state.Error)
}
if value == nil {
p.state.Write(G.nullValue)
return
}
p.state.Write(value)
}
func (p *Packer) PackBinary (value []byte) {
if !p.state.SetValue() {
panic(p.state.Error)
}
if value == nil {
p.state.Write(G.nullValue)
return
}
if len(value) == 0 {
p.state.Write2Byte('"','"')
return
}
p.state.WriteByte('"')
ln := base64.StdEncoding.EncodedLen(len(value))
if ln <= len(p.state.Temp) {
dst := p.state.Temp[:ln]
base64.StdEncoding.Encode(dst, value)
p.state.Write(dst)
} else if ln <= 1024 {
dst := make([]byte, ln)
base64.StdEncoding.Encode(dst, value)
p.state.Write(dst)
} else {
enc := base64.NewEncoder(base64.StdEncoding, p.state)
enc.Write(value)
enc.Close()
}
p.state.WriteByte('"')
}
func (p *Packer) PackString (value string) {
if !p.state.SetValue() {
panic(p.state.Error)
}
if len(value) == 0 {
p.state.Write2Byte('"','"')
} else {
p.string(value,p.options.EscapeHTML())
}
}
func (p *Packer) PackBool (value bool) {
if !p.state.SetValue() {
panic(p.state.Error)
}
if p.options.QuoteNumbers() {
p.state.WriteByte('"')
}
if value {
p.state.Write(G.trueValue)
} else {
p.state.Write(G.falseValue)
}
if p.options.QuoteNumbers() {
p.state.WriteByte('"')
}
}
func (p *Packer) PackInt8 (value int8) {}
func (p *Packer) PackInt16 (value int16) {}
func (p *Packer) PackInt32 (value int32) {}
func (p *Packer) PackInt64 (value int64) {
if !p.state.SetValue() {
panic(p.state.Error)
}
if p.options.QuoteNumbers() {
p.state.WriteByte('"')
}
if value == 0 {
p.state.WriteByte('0')
} else {
p.state.WriteIntStr(value, 10)
}
if p.options.QuoteNumbers() {
p.state.WriteByte('"')
}
}
func (p *Packer) PackUInt8 (value uint8) {}
func (p *Packer) PackUInt16 (value uint16) {}
func (p *Packer) PackUInt32 (value uint32) {}
func (p *Packer) PackUInt64 (value uint64) {
if !p.state.SetValue() {
panic(p.state.Error)
}
if p.options.QuoteNumbers() {
p.state.WriteByte('"')
}
if value == 0 {
p.state.WriteByte('0')
} else {
p.state.WriteUIntStr(value, 10)
}
if p.options.QuoteNumbers() {
p.state.WriteByte('"')
}
}
func (p *Packer) PackFloat32 (value float32) {}
func (p *Packer) PackFloat64 (value float64) {
if !p.state.SetValue() {
panic(p.state.Error)
}
if math.IsInf(value,0) || math.IsNaN(value) {
p.state.SetError(packets.G.ErrInvalidValue)
return
}
if p.options.QuoteNumbers() {
p.state.WriteByte('"')
}
switch {
case value == 0 : p.state.Write(G.zeroValue)
case math.IsNaN(value) : p.state.Write(G.floatNaN)
case math.IsInf(value, 1) : p.state.Write(G.floatInfPos)
case math.IsInf(value, -1) : p.state.Write(G.floatInfNeg)
default:
fm := byte('f')
if ab := math.Abs(value); ab != 0 {
if ab < 1e-6 || ab >= 1e21 {
fm = 'e'
}
}
b := p.state.Temp[:0]
b = strconv.AppendFloat(b, value, fm, -1, 64)
if fm == 'e' {
// clean up e-09 to e-9
n := len(b)
if n >= 4 && b[n-4] == 'e' && b[n-3] == '-' && b[n-2] == '0' {
b[n-2] = b[n-1]
b = b[:n-1]
}
}
p.state.Write(b)
}
if p.options.QuoteNumbers() {
p.state.WriteByte('"')
}
}
func (p *Packer) PackDate (value time.Time) {
if !p.state.SetValue() {
panic(p.state.Error)
}
p.state.WriteByte('"')
p.state.AppendWith(bins.AppendFnc(func(d []byte) []byte {
return value.AppendFormat(d,p.options.dateLayout)
}))
p.state.WriteByte('"')
}
func (p *Packer) PackList (builder func(packer packets.ListPacker)) {
p.packCompound(packets.NodeKindList,'[',']',func(pk *Packer) { builder (pk) })
}
func (p *Packer) PackMap (builder func(packer packets.MapPacker )) {
p.packCompound(packets.NodeKindMap,'{','}',func(pk *Packer) { builder (pk) })
}
func (p *Packer) PackObject(builder func(packer packets.ObjectPacker)) {
p.packCompound(packets.NodeKindObject,'{','}',func(pk *Packer) { builder (pk) })
}
func (p *Packer) packCompound(kind packets.NodeKind, opening, closing byte, buildFnc func(*Packer)) {
if !p.state.SetValue() {
panic(p.state.Error)
}
dn := false
defer func(d *bool) {
if !(*d) {
p.state.SetError(errs.ToError(recover()))
}
}(&dn)
if p.state.Open(kind) {
p.addSep()
p.state.WriteByte(opening)
buildFnc(p)
if p.state.Error == nil {
if p.state.Close() {
p.state.WriteByte(closing)
}
}
}
dn = true
}
func (p *Packer) string(s string, scapeHTML bool) {
p.state.WriteByte('"')
start := 0
for i := 0; i < len(s); {
if b := s[i]; b < utf8.RuneSelf {
if !G.jsonChars[b].isHtmlUnsafe() || (!scapeHTML && !G.jsonChars[b].isUnsafe()) {
i++
continue
}
if start < i {
p.state.WriteString(s[start:i])
}
p.state.WriteByte('\\')
switch b {
case '\\', '"': p.state.WriteByte(b)
case '\n': p.state.WriteByte('n')
case '\r': p.state.WriteByte('r')
case '\t': p.state.WriteByte('t')
default:
p.state.WriteString(`u00`)
p.state.Write2Byte(G.hex[b>>4], G.hex[b&0xF])
}
i++
start = i
continue
}
c, size := utf8.DecodeRuneInString(s[i:])
if c == utf8.RuneError && size == 1 {
if start < i {
p.state.WriteString(s[start:i])
}
p.state.WriteString(`\ufffd`)
i += size
start = i
continue
}
if c == '\u2028' || c == '\u2029' {
if start < i {
p.state.WriteString(s[start:i])
}
p.state.WriteString(`\u202`)
p.state.WriteByte(G.hex[c&0xF])
i += size
start = i
continue
}
i += size
}
if start < len(s) {
p.state.WriteString(s[start:])
}
p.state.WriteByte('"')
}
func (p *Packer) addSep () *Packer {
if p.state.Node.Count > 0 {
p.state.WriteByte(',')
}
return p
}

Powered by TurnKey Linux.