gotype/unfold_refl.go (168 lines of code) (raw):

// Licensed to Elasticsearch B.V. under one or more contributor // license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright // ownership. Elasticsearch B.V. licenses this file to you 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 gotype import ( "reflect" "unsafe" structform "github.com/elastic/go-structform" ) type liftedReflUnfolder struct{ unfolder ptrUnfolder } type unfolderReflSlice struct { elem reflUnfolder } type unfolderReflSliceStart struct { unfolderErrArrayStart } type unfolderReflMap struct { shared unfolderReflMapShared } type unfolderReflMapShared struct { waitKey *unfolderReflMapOnKey waitElem *unfolderReflMapOnElem } type unfolderReflMapStart struct { unfolderErrObjectStart } type unfolderReflMapOnKey struct { unfolderErrExpectKey shared *unfolderReflMapShared } type unfolderReflMapOnElem struct { shared *unfolderReflMapShared elem reflUnfolder } type unfolderReflPtr struct { elem reflUnfolder } var ( _singletonUnfolderReflSliceStart = &unfolderReflSliceStart{} _singletonUnfolderReflMapStart = &unfolderReflMapStart{} ) func liftGoUnfolder(u ptrUnfolder) *liftedReflUnfolder { return &liftedReflUnfolder{u} } func (u *liftedReflUnfolder) initState(ctx *unfoldCtx, v reflect.Value) { ptr := unsafe.Pointer(v.Pointer()) u.unfolder.initState(ctx, ptr) } func newUnfolderReflSlice(elem reflUnfolder) *unfolderReflSlice { return &unfolderReflSlice{elem} } func (u *unfolderReflSlice) initState(ctx *unfoldCtx, v reflect.Value) { ctx.value.push(v) ctx.unfolder.push(u) ctx.idx.push(0) ctx.unfolder.push(_singletonUnfolderReflSliceStart) } func (u *unfolderReflSlice) cleanup(ctx *unfoldCtx) { ctx.idx.pop() ctx.value.pop() ctx.unfolder.pop() } func (u *unfolderReflSliceStart) cleanup(ctx *unfoldCtx) { ctx.unfolder.pop() } func (u *unfolderReflSliceStart) OnArrayStart(ctx *unfoldCtx, l int, baseType structform.BaseType) error { ptr := ctx.value.current v := ptr.Elem() if l < 0 { l = 0 } if v.IsNil() && l > 0 { v.Set(reflect.MakeSlice(v.Type(), l, l)) } else if !v.IsNil() && l < v.Len() { v.SetLen(l) } u.cleanup(ctx) return nil } func (u *unfolderReflSlice) OnArrayFinished(ctx *unfoldCtx) error { u.cleanup(ctx) return nil } func (u *unfolderReflSlice) prepare(ctx *unfoldCtx) reflect.Value { // make space for some more element ptr := ctx.value.current idx := &ctx.idx v := ptr.Elem() switch { case v.Len() > idx.current: case v.Cap() > idx.current: v.SetLen(idx.current + 1) default: v.Set(reflect.Append(v, reflect.Zero(v.Type().Elem()))) } elem := v.Index(idx.current).Addr() idx.current++ return elem } func (u *unfolderReflSlice) OnObjectFinished(_ *unfoldCtx) error { return errUnsupported } func newUnfolderReflMap(elem reflUnfolder) *unfolderReflMap { u := &unfolderReflMap{} u.shared.waitKey = &unfolderReflMapOnKey{shared: &u.shared} u.shared.waitElem = &unfolderReflMapOnElem{shared: &u.shared, elem: elem} return u } func (u *unfolderReflMap) initState(ctx *unfoldCtx, v reflect.Value) { ctx.value.push(v) ctx.unfolder.push(u.shared.waitKey) ctx.unfolder.push(_singletonUnfolderReflMapStart) } func (u *unfolderReflMapStart) OnObjectStart(ctx *unfoldCtx, l int, bt structform.BaseType) error { ctx.unfolder.pop() return nil } func (u *unfolderReflMapOnKey) OnKey(ctx *unfoldCtx, key string) error { ctx.key.push(key) ctx.unfolder.current = u.shared.waitElem return nil } func (u *unfolderReflMapOnKey) OnKeyRef(ctx *unfoldCtx, key []byte) error { return u.OnKey(ctx, ctx.keyCache.get(key)) } func (u *unfolderReflMapOnKey) OnObjectFinished(ctx *unfoldCtx) error { ctx.unfolder.pop() ctx.value.pop() return nil } func (u *unfolderReflMapOnElem) prepare(ctx *unfoldCtx) reflect.Value { ptr := ctx.value.current v := ptr.Elem() et := v.Type().Elem() target := reflect.New(et) ctx.value.push(target) return target } func (u *unfolderReflMapOnElem) process(ctx *unfoldCtx) { ptr := ctx.value.pop() v := ptr.Elem() ptr = ctx.value.current m := ptr.Elem() m.SetMapIndex(reflect.ValueOf(ctx.key.pop()), v) ctx.unfolder.current = u.shared.waitKey } func (u *unfolderReflMapOnElem) OnObjectFinished(_ *unfoldCtx) error { return errExpectedObjectValue } func (u *unfolderReflMapOnElem) OnArrayFinished(_ *unfoldCtx) error { return errUnsupported } func newUnfolderReflPtr(elem reflUnfolder) *unfolderReflPtr { return &unfolderReflPtr{elem} } func (u *unfolderReflPtr) initState(ctx *unfoldCtx, v reflect.Value) { ctx.value.push(v) ctx.unfolder.push(u) } func (u *unfolderReflPtr) cleanup(ctx *unfoldCtx) { ctx.value.pop() ctx.unfolder.pop() } func (u *unfolderReflPtr) prepare(ctx *unfoldCtx) reflect.Value { ptr := ctx.value.current v := ptr.Elem() target := reflect.New(v.Type().Elem()) ctx.value.push(target) return target } func (u *unfolderReflPtr) process(ctx *unfoldCtx) { v := ctx.value.pop() ptr := ctx.value.current.Elem() ptr.Set(v) u.cleanup(ctx) } func (u *unfolderReflPtr) OnObjectFinished(_ *unfoldCtx) error { return errUnsupported } func (u *unfolderReflPtr) OnArrayFinished(_ *unfoldCtx) error { return errUnsupported }