| // Copyright 2017 The Wuffs Authors. |
| // |
| // 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 |
| // |
| // https://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 cgen |
| |
| import ( |
| "errors" |
| "fmt" |
| "math/big" |
| "strings" |
| |
| a "github.com/google/wuffs/lang/ast" |
| t "github.com/google/wuffs/lang/token" |
| ) |
| |
| var ( |
| errNoSuchBuiltin = errors.New("cgen: internal error: no such built-in") |
| errOptimizationNotApplicable = errors.New("cgen: internal error: optimization not applicable") |
| ) |
| |
| func (g *gen) writeBuiltinCall(b *buffer, n *a.Expr, depth uint32) error { |
| if n.Operator() != t.IDOpenParen { |
| return errNoSuchBuiltin |
| } |
| method := n.LHS().AsExpr() |
| recv := method.LHS().AsExpr() |
| recvTyp := recv.MType() |
| |
| switch recvTyp.Decorator() { |
| case 0: |
| // No-op. |
| case t.IDNptr, t.IDPtr: |
| // TODO: don't hard-code set. |
| if method.Ident() != t.IDSet { |
| return errNoSuchBuiltin |
| } |
| // TODO: don't hard-code a_dst. |
| b.printf("wuffs_base__image_config__set(a_dst") |
| for _, o := range n.Args() { |
| b.writeb(',') |
| if err := g.writeExpr(b, o.AsArg().Value(), depth); err != nil { |
| return err |
| } |
| } |
| b.printf(")") |
| return nil |
| case t.IDSlice: |
| return g.writeBuiltinSlice(b, recv, method.Ident(), n.Args(), depth) |
| case t.IDTable: |
| return g.writeBuiltinTable(b, recv, method.Ident(), n.Args(), depth) |
| default: |
| return errNoSuchBuiltin |
| } |
| |
| qid := recvTyp.QID() |
| if qid[0] != t.IDBase { |
| return errNoSuchBuiltin |
| } |
| |
| if qid[1].IsNumType() { |
| return g.writeBuiltinNumType(b, recv, method.Ident(), n.Args(), depth) |
| } else { |
| switch qid[1] { |
| case t.IDIOReader: |
| return g.writeBuiltinIOReader(b, recv, method.Ident(), n.Args(), depth) |
| case t.IDIOWriter: |
| return g.writeBuiltinIOWriter(b, recv, method.Ident(), n.Args(), depth) |
| } |
| } |
| return errNoSuchBuiltin |
| } |
| |
| func (g *gen) writeBuiltinIO(b *buffer, recv *a.Expr, method t.ID, args []*a.Node, depth uint32) error { |
| switch method { |
| case t.IDAvailable: |
| p0, p1 := "", "" |
| // TODO: don't hard-code these. |
| switch recv.Str(g.tm) { |
| case "args.dst": |
| p0 = "io1_a_dst" |
| p1 = "iop_a_dst" |
| case "args.src": |
| p0 = "io1_a_src" |
| p1 = "iop_a_src" |
| case "w": |
| p0 = "io1_v_w" |
| p1 = "iop_v_w" |
| } |
| if p0 == "" { |
| return fmt.Errorf(`TODO: cgen a "foo.available" expression`) |
| } |
| b.printf("((uint64_t)(%s - %s))", p0, p1) |
| return nil |
| |
| case t.IDSet: |
| typ, v := "reader", "r" |
| if len(args) == 1 { |
| typ, v = "writer", "w" |
| } |
| b.printf("wuffs_base__io_%s__set(&v_%s, &u_%s, &iop_v_%s, &io1_v_%s,", typ, v, v, v, v) |
| return g.writeArgs(b, args, depth) |
| |
| } |
| return errNoSuchBuiltin |
| } |
| |
| func (g *gen) writeBuiltinIOReader(b *buffer, recv *a.Expr, method t.ID, args []*a.Node, depth uint32) error { |
| // TODO: don't hard-code the recv being a_src. |
| switch method { |
| case t.IDUndoByte: |
| b.writes("(iop_a_src--, wuffs_base__return_empty_struct())") |
| return nil |
| |
| case t.IDCanUndoByte: |
| b.writes("(iop_a_src > io0_a_src)") |
| return nil |
| |
| case t.IDPosition: |
| b.printf("(a_src.private_impl.buf ? wuffs_base__u64__sat_add(" + |
| "a_src.private_impl.buf->meta.pos, iop_a_src - a_src.private_impl.buf->data.ptr) : 0)") |
| return nil |
| |
| case t.IDSetLimit: |
| b.printf("wuffs_base__io_reader__set_limit(&%ssrc, iop_a_src,", aPrefix) |
| // TODO: update the iop variables? |
| return g.writeArgs(b, args, depth) |
| |
| case t.IDSetMark: |
| b.printf("wuffs_base__io_reader__set_mark(&%ssrc, iop_a_src)", aPrefix) |
| return nil |
| |
| case t.IDSinceMark, t.IDSinceMarkLength: |
| prefix, name := aPrefix, "src" |
| if recv.Operator() == 0 { |
| prefix, name = vPrefix, recv.Ident().Str(g.tm) |
| } |
| |
| if method == t.IDSinceMark { |
| b.printf("((wuffs_base__slice_u8){ "+ |
| ".ptr = %s%s.private_impl.mark, "+ |
| ".len = (size_t)(", |
| prefix, name) |
| } |
| b.printf("iop_%s%s - %s%s.private_impl.mark", prefix, name, prefix, name) |
| if method == t.IDSinceMark { |
| b.writes("), })") |
| } |
| return nil |
| |
| case t.IDSkipFast: |
| // Generate a two part expression using the comma operator: "(pointer |
| // increment, return_empty_struct call)". The final part is a function |
| // call (to a static inline function) instead of a struct literal, to |
| // avoid a "expression result unused" compiler error. |
| b.writes("(iop_a_src += ") |
| if err := g.writeExpr(b, args[0].AsArg().Value(), depth); err != nil { |
| return err |
| } |
| b.writes(", wuffs_base__return_empty_struct())") |
| return nil |
| |
| case t.IDTake: |
| b.printf("wuffs_base__io_reader__take(&iop_a_src, io1_a_src,") |
| return g.writeArgs(b, args, depth) |
| } |
| |
| if method >= peekMethodsBase { |
| if m := method - peekMethodsBase; m < t.ID(len(peekMethods)) { |
| if p := peekMethods[m]; p.n != 0 { |
| if p.size != p.n { |
| b.printf("((uint%d_t)(", p.size) |
| } |
| b.printf("wuffs_base__load_u%d%ce(iop_a_src)", p.n, p.endianness) |
| if p.size != p.n { |
| b.writes("))") |
| } |
| return nil |
| } |
| } |
| } |
| |
| return g.writeBuiltinIO(b, recv, method, args, depth) |
| } |
| |
| func (g *gen) writeBuiltinIOWriter(b *buffer, recv *a.Expr, method t.ID, args []*a.Node, depth uint32) error { |
| // TODO: don't hard-code the recv being a_dst or w. |
| switch method { |
| case t.IDCopyNFromHistory, t.IDCopyNFromHistoryFast: |
| suffix := "" |
| if method == t.IDCopyNFromHistoryFast { |
| suffix = "_fast" |
| } |
| b.printf("wuffs_base__io_writer__copy_n_from_history%s("+ |
| "&iop_a_dst, %sdst.private_impl.mark, io1_a_dst", |
| suffix, aPrefix) |
| for _, o := range args { |
| b.writeb(',') |
| if err := g.writeExpr(b, o.AsArg().Value(), depth); err != nil { |
| return err |
| } |
| } |
| b.writeb(')') |
| return nil |
| |
| case t.IDCopyNFromReader: |
| b.printf("wuffs_base__io_writer__copy_n_from_reader(&iop_a_dst, io1_a_dst,") |
| if err := g.writeExpr(b, args[0].AsArg().Value(), depth); err != nil { |
| return err |
| } |
| // TODO: don't assume that the last argument is "args.src". |
| b.printf(", &iop_a_src, io1_a_src)") |
| return nil |
| |
| case t.IDCopyFromSlice: |
| b.printf("wuffs_base__io_writer__copy_from_slice(&iop_a_dst, io1_a_dst,") |
| return g.writeArgs(b, args, depth) |
| |
| case t.IDCopyNFromSlice: |
| b.printf("wuffs_base__io_writer__copy_n_from_slice(&iop_a_dst, io1_a_dst,") |
| return g.writeArgs(b, args, depth) |
| |
| case t.IDPosition: |
| b.printf("(a_dst.private_impl.buf ? wuffs_base__u64__sat_add(" + |
| "a_dst.private_impl.buf->meta.pos, iop_a_dst - a_dst.private_impl.buf->data.ptr) : 0)") |
| return nil |
| |
| case t.IDSetMark: |
| // TODO: is a private_impl.mark the right representation? What |
| // if the function is passed a (ptr io_writer) instead of a |
| // (io_writer)? Do we still want to have that mark live outside of |
| // the function scope? |
| b.printf("wuffs_base__io_writer__set_mark(&%sdst, iop_a_dst)", aPrefix) |
| return nil |
| |
| case t.IDSinceMark, t.IDSinceMarkLength: |
| prefix, name := aPrefix, "dst" |
| if recv.Operator() == 0 { |
| prefix, name = vPrefix, recv.Ident().Str(g.tm) |
| } |
| |
| if method == t.IDSinceMark { |
| b.printf("((wuffs_base__slice_u8){ "+ |
| ".ptr = %s%s.private_impl.mark, "+ |
| ".len = (size_t)(", |
| prefix, name) |
| } |
| b.printf("iop_%s%s - %s%s.private_impl.mark", prefix, name, prefix, name) |
| if method == t.IDSinceMark { |
| b.writes("), })") |
| } |
| return nil |
| } |
| |
| if method >= writeFastMethodsBase { |
| if m := method - writeFastMethodsBase; m < t.ID(len(writeFastMethods)) { |
| if p := writeFastMethods[m]; p.n != 0 { |
| // Generate a three part expression using the comma operator: |
| // "(store, pointer increment, return_empty_struct call)". The |
| // final part is a function call (to a static inline function) |
| // instead of a struct literal, to avoid a "expression result |
| // unused" compiler error. |
| b.printf("(wuffs_base__store_u%d%ce(iop_a_dst,", p.n, p.endianness) |
| if err := g.writeExpr(b, args[0].AsArg().Value(), depth); err != nil { |
| return err |
| } |
| b.printf("), iop_a_dst += %d, wuffs_base__return_empty_struct())", p.n/8) |
| return nil |
| } |
| } |
| } |
| |
| return g.writeBuiltinIO(b, recv, method, args, depth) |
| } |
| |
| func (g *gen) writeBuiltinNumType(b *buffer, recv *a.Expr, method t.ID, args []*a.Node, depth uint32) error { |
| switch method { |
| case t.IDLowBits: |
| // "recv.low_bits(n:etc)" in C is one of: |
| // - "((recv) & constant)" |
| // - "((recv) & WUFFS_BASE__LOW_BITS_MASK__UXX(n))" |
| b.writes("((") |
| if err := g.writeExpr(b, recv, depth); err != nil { |
| return err |
| } |
| b.writes(") & ") |
| |
| if cv := args[0].AsArg().Value().ConstValue(); cv != nil && cv.Sign() >= 0 && cv.Cmp(sixtyFour) <= 0 { |
| mask := big.NewInt(0) |
| mask.Lsh(one, uint(cv.Uint64())) |
| mask.Sub(mask, one) |
| b.printf("0x%s", strings.ToUpper(mask.Text(16))) |
| } else { |
| if sz, err := g.sizeof(recv.MType()); err != nil { |
| return err |
| } else { |
| b.printf("WUFFS_BASE__LOW_BITS_MASK__U%d(", 8*sz) |
| } |
| if err := g.writeExpr(b, args[0].AsArg().Value(), depth); err != nil { |
| return err |
| } |
| b.writes(")") |
| } |
| |
| b.writes(")") |
| return nil |
| |
| case t.IDHighBits: |
| // "recv.high_bits(n:etc)" in C is "((recv) >> (8*sizeof(recv) - (n)))". |
| b.writes("((") |
| if err := g.writeExpr(b, recv, depth); err != nil { |
| return err |
| } |
| b.writes(") >> (") |
| if sz, err := g.sizeof(recv.MType()); err != nil { |
| return err |
| } else { |
| b.printf("%d", 8*sz) |
| } |
| b.writes(" - (") |
| if err := g.writeExpr(b, args[0].AsArg().Value(), depth); err != nil { |
| return err |
| } |
| b.writes(")))") |
| return nil |
| |
| case t.IDMax: |
| b.writes("wuffs_base__u") |
| if sz, err := g.sizeof(recv.MType()); err != nil { |
| return err |
| } else { |
| b.printf("%d", 8*sz) |
| } |
| b.writes("__max(") |
| if err := g.writeExpr(b, recv, depth); err != nil { |
| return err |
| } |
| b.writes(",") |
| if err := g.writeExpr(b, args[0].AsArg().Value(), depth); err != nil { |
| return err |
| } |
| b.writes(")") |
| return nil |
| |
| case t.IDMin: |
| b.writes("wuffs_base__u") |
| if sz, err := g.sizeof(recv.MType()); err != nil { |
| return err |
| } else { |
| b.printf("%d", 8*sz) |
| } |
| b.writes("__min(") |
| if err := g.writeExpr(b, recv, depth); err != nil { |
| return err |
| } |
| b.writes(",") |
| if err := g.writeExpr(b, args[0].AsArg().Value(), depth); err != nil { |
| return err |
| } |
| b.writes(")") |
| return nil |
| } |
| return errNoSuchBuiltin |
| } |
| |
| func (g *gen) writeBuiltinSlice(b *buffer, recv *a.Expr, method t.ID, args []*a.Node, depth uint32) error { |
| switch method { |
| case t.IDCopyFromSlice: |
| if err := g.writeBuiltinSliceCopyFromSlice8(b, recv, method, args, depth); err != errOptimizationNotApplicable { |
| return err |
| } |
| |
| // TODO: don't assume that the slice is a slice of base.u8. |
| b.writes("wuffs_base__slice_u8__copy_from_slice(") |
| if err := g.writeExpr(b, recv, depth); err != nil { |
| return err |
| } |
| b.writeb(',') |
| return g.writeArgs(b, args, depth) |
| |
| case t.IDLength: |
| if recv.Operator() == t.IDOpenParen { |
| if method := recv.LHS().AsExpr(); method.Operator() == t.IDDot && method.Ident() == t.IDSinceMark { |
| if lhs := method.LHS().AsExpr(); lhs.MType().IsIOType() { |
| b.writes("((uint64_t)(") |
| if lhs.MType().QID()[1] == t.IDIOReader { |
| if err := g.writeBuiltinIOReader(b, lhs, t.IDSinceMarkLength, nil, depth); err != nil { |
| return err |
| } |
| } else { |
| if err := g.writeBuiltinIOWriter(b, lhs, t.IDSinceMarkLength, nil, depth); err != nil { |
| return err |
| } |
| } |
| b.writes("))") |
| return nil |
| } |
| } |
| } |
| |
| b.writes("((uint64_t)(") |
| if err := g.writeExpr(b, recv, depth); err != nil { |
| return err |
| } |
| b.writes(".len))") |
| return nil |
| |
| case t.IDSuffix: |
| // TODO: don't assume that the slice is a slice of base.u8. |
| b.writes("wuffs_base__slice_u8__suffix(") |
| if err := g.writeExpr(b, recv, depth); err != nil { |
| return err |
| } |
| b.writeb(',') |
| return g.writeArgs(b, args, depth) |
| } |
| return errNoSuchBuiltin |
| } |
| |
| // writeBuiltinSliceCopyFromSlice8 writes an optimized version of: |
| // |
| // foo[fIndex:fIndex + 8].copy_from_slice!(s:bar[bIndex:bIndex + 8]) |
| func (g *gen) writeBuiltinSliceCopyFromSlice8(b *buffer, recv *a.Expr, method t.ID, args []*a.Node, depth uint32) error { |
| if method != t.IDCopyFromSlice || len(args) != 1 { |
| return errOptimizationNotApplicable |
| } |
| foo, fIndex := matchFooIndexIndexPlus8(recv) |
| bar, bIndex := matchFooIndexIndexPlus8(args[0].AsArg().Value()) |
| if foo == nil || bar == nil { |
| return errOptimizationNotApplicable |
| } |
| b.writes("memcpy((") |
| if err := g.writeExpr(b, foo, depth); err != nil { |
| return err |
| } |
| if fIndex != nil { |
| b.writes(")+(") |
| if err := g.writeExpr(b, fIndex, depth); err != nil { |
| return err |
| } |
| } |
| b.writes("),(") |
| if err := g.writeExpr(b, bar, depth); err != nil { |
| return err |
| } |
| if bIndex != nil { |
| b.writes(")+(") |
| if err := g.writeExpr(b, bIndex, depth); err != nil { |
| return err |
| } |
| } |
| // TODO: don't assume that the slice is a slice of base.u8. |
| b.writes("), 8)") |
| return nil |
| } |
| |
| // matchFooIndexIndexPlus8 matches n with "foo[index:index + 8]" or "foo[:8]". |
| // It returns a nil foo if there isn't a match. |
| func matchFooIndexIndexPlus8(n *a.Expr) (foo *a.Expr, index *a.Expr) { |
| if n.Operator() != t.IDColon { |
| return nil, nil |
| } |
| foo = n.LHS().AsExpr() |
| index = n.MHS().AsExpr() |
| rhs := n.RHS().AsExpr() |
| if rhs == nil { |
| return nil, nil |
| } |
| |
| if index == nil { |
| // No-op. |
| } else if rhs.Operator() != t.IDXBinaryPlus || !rhs.LHS().AsExpr().Eq(index) { |
| return nil, nil |
| } else { |
| rhs = rhs.RHS().AsExpr() |
| } |
| |
| if cv := rhs.ConstValue(); cv == nil || cv.Cmp(eight) != 0 { |
| return nil, nil |
| } |
| return foo, index |
| } |
| |
| func (g *gen) writeBuiltinTable(b *buffer, recv *a.Expr, method t.ID, args []*a.Node, depth uint32) error { |
| field := "" |
| |
| switch method { |
| case t.IDHeight: |
| field = "height" |
| case t.IDStride: |
| field = "stride" |
| case t.IDWidth: |
| field = "width" |
| |
| case t.IDRow: |
| // TODO: don't assume that the table is a table of base.u8. |
| b.writes("wuffs_base__table_u8__row(") |
| if err := g.writeExpr(b, recv, depth); err != nil { |
| return err |
| } |
| b.writeb(',') |
| return g.writeArgs(b, args, depth) |
| } |
| |
| if field != "" { |
| b.writes("((uint64_t)(") |
| if err := g.writeExpr(b, recv, depth); err != nil { |
| return err |
| } |
| b.printf(".%s))", field) |
| return nil |
| } |
| |
| return errNoSuchBuiltin |
| } |
| |
| func (g *gen) writeArgs(b *buffer, args []*a.Node, depth uint32) error { |
| for i, o := range args { |
| if i > 0 { |
| b.writeb(',') |
| } |
| if err := g.writeExpr(b, o.AsArg().Value(), depth); err != nil { |
| return err |
| } |
| } |
| b.writes(")") |
| return nil |
| } |
| |
| func (g *gen) writeBuiltinQuestionCall(b *buffer, n *a.Expr, depth uint32) error { |
| // TODO: also handle (or reject??) being on the RHS of an =? operator. |
| if (n.Operator() != t.IDOpenParen) || (!n.Effect().Coroutine()) { |
| return errNoSuchBuiltin |
| } |
| method := n.LHS().AsExpr() |
| recv := method.LHS().AsExpr() |
| recvTyp := recv.MType() |
| if !recvTyp.IsIOType() { |
| return errNoSuchBuiltin |
| } |
| |
| if recvTyp.QID()[1] == t.IDIOReader { |
| switch method.Ident() { |
| case t.IDReadU8, t.IDReadU8AsU32, t.IDReadU8AsU64: |
| if err := g.writeCoroSuspPoint(b, false); err != nil { |
| return err |
| } |
| if g.currFunk.tempW > maxTemp { |
| return fmt.Errorf("too many temporary variables required") |
| } |
| temp := g.currFunk.tempW |
| g.currFunk.tempW++ |
| |
| b.printf("if (WUFFS_BASE__UNLIKELY(iop_a_src == io1_a_src)) {" + |
| "status = wuffs_base__suspension__short_read; goto suspend; }") |
| |
| // TODO: watch for passing an array type to writeCTypeName? In C, an |
| // array type can decay into a pointer. |
| if err := g.writeCTypeName(b, n.MType(), tPrefix, fmt.Sprint(temp)); err != nil { |
| return err |
| } |
| b.printf(" = *iop_a_src++;\n") |
| return nil |
| |
| case t.IDSkip: |
| x := n.Args()[0].AsArg().Value() |
| if cv := x.ConstValue(); cv != nil && cv.Cmp(one) == 0 { |
| if err := g.writeCoroSuspPoint(b, false); err != nil { |
| return err |
| } |
| b.printf("if (WUFFS_BASE__UNLIKELY(iop_a_src == io1_a_src)) {" + |
| "status = wuffs_base__suspension__short_read; goto suspend; }") |
| b.printf("iop_a_src++;\n") |
| return nil |
| } |
| |
| g.currFunk.usesScratch = true |
| // TODO: don't hard-code [0], and allow recursive coroutines. |
| scratchName := fmt.Sprintf("self->private_impl.%s%s[0].scratch", |
| cPrefix, g.currFunk.astFunc.FuncName().Str(g.tm)) |
| |
| b.printf("%s = ", scratchName) |
| if err := g.writeExpr(b, x, depth); err != nil { |
| return err |
| } |
| b.writes(";\n") |
| |
| if err := g.writeCoroSuspPoint(b, false); err != nil { |
| return err |
| } |
| |
| b.printf("if (%s > ((uint64_t)(io1_a_src - iop_a_src))) {\n", scratchName) |
| b.printf("%s -= io1_a_src - iop_a_src;\n", scratchName) |
| b.printf("iop_a_src = io1_a_src;\n") |
| |
| b.writes("status = wuffs_base__suspension__short_read; goto suspend; }\n") |
| b.printf("iop_a_src += %s;\n", scratchName) |
| return nil |
| } |
| |
| if method.Ident() >= readMethodsBase { |
| if m := method.Ident() - readMethodsBase; m < t.ID(len(readMethods)) { |
| if p := readMethods[m]; p.n != 0 { |
| if err := g.writeCoroSuspPoint(b, false); err != nil { |
| return err |
| } |
| return g.writeReadUxxAsUyy(b, n, "a_src", p.n, p.size, p.endianness) |
| } |
| } |
| } |
| |
| } else { |
| switch method.Ident() { |
| case t.IDWriteU8: |
| if err := g.writeCoroSuspPoint(b, false); err != nil { |
| return err |
| } |
| b.writes("if (iop_a_dst == io1_a_dst) {\n" + |
| "status = wuffs_base__suspension__short_write; goto suspend; }\n" + |
| "*iop_a_dst++ = ") |
| x := n.Args()[0].AsArg().Value() |
| if err := g.writeExpr(b, x, depth); err != nil { |
| return err |
| } |
| b.writes(";\n") |
| return nil |
| } |
| } |
| return errNoSuchBuiltin |
| } |
| |
| func (g *gen) writeReadUxxAsUyy(b *buffer, n *a.Expr, preName string, xx uint8, yy uint8, endianness uint8) error { |
| if (xx&7 != 0) || (xx < 16) || (xx > 64) { |
| return fmt.Errorf("internal error: bad writeReadUXX size %d", xx) |
| } |
| if endianness != 'b' && endianness != 'l' { |
| return fmt.Errorf("internal error: bad writeReadUXX endianness %q", endianness) |
| } |
| |
| if g.currFunk.tempW > maxTemp { |
| return fmt.Errorf("too many temporary variables required") |
| } |
| temp := g.currFunk.tempW |
| g.currFunk.tempW++ |
| |
| if err := g.writeCTypeName(b, n.MType(), tPrefix, fmt.Sprint(temp)); err != nil { |
| return err |
| } |
| b.writes(";") |
| |
| g.currFunk.usesScratch = true |
| // TODO: don't hard-code [0], and allow recursive coroutines. |
| scratchName := fmt.Sprintf("self->private_impl.%s%s[0].scratch", |
| cPrefix, g.currFunk.astFunc.FuncName().Str(g.tm)) |
| |
| b.printf("if (WUFFS_BASE__LIKELY(io1_a_src - iop_a_src >= %d)) {", xx/8) |
| b.printf("%s%d =", tPrefix, temp) |
| if xx != yy { |
| b.printf("((uint%d_t)(", yy) |
| } |
| b.printf("wuffs_base__load_u%d%ce(iop_a_src)", xx, endianness) |
| if xx != yy { |
| b.writes("))") |
| } |
| b.printf("; iop_a_src += %d;\n", xx/8) |
| b.printf("} else {") |
| |
| b.printf("%s = 0;\n", scratchName) |
| if err := g.writeCoroSuspPoint(b, false); err != nil { |
| return err |
| } |
| b.printf("while (true) {") |
| |
| b.printf("if (WUFFS_BASE__UNLIKELY(iop_%s == io1_%s)) {"+ |
| "status = wuffs_base__suspension__short_read; goto suspend; }", |
| preName, preName) |
| |
| b.printf("uint64_t *scratch = &%s;", scratchName) |
| b.printf("uint32_t num_bits_%d = *scratch", temp) |
| switch endianness { |
| case 'b': |
| b.writes("& 0xFF; *scratch >>= 8; *scratch <<= 8;") |
| b.printf("*scratch |= ((uint64_t)(*%s%s++)) << (56 - num_bits_%d);", |
| iopPrefix, preName, temp) |
| case 'l': |
| b.writes(">> 56; *scratch <<= 8; *scratch >>= 8;") |
| b.printf("*scratch |= ((uint64_t)(*%s%s++)) << num_bits_%d;", |
| iopPrefix, preName, temp) |
| } |
| |
| b.printf("if (num_bits_%d == %d) { %s%d =", temp, xx-8, tPrefix, temp) |
| if xx != yy { |
| b.printf("((uint%d_t)(", yy) |
| } |
| switch endianness { |
| case 'b': |
| b.printf("*scratch >> %d", 64-xx) |
| case 'l': |
| b.printf("*scratch") |
| } |
| if xx != yy { |
| b.writes("))") |
| } |
| b.printf("; break;") |
| b.printf("}") |
| |
| b.printf("num_bits_%d += 8;", temp) |
| switch endianness { |
| case 'b': |
| b.printf("*scratch |= ((uint64_t)(num_bits_%d));", temp) |
| case 'l': |
| b.printf("*scratch |= ((uint64_t)(num_bits_%d)) << 56;", temp) |
| } |
| |
| b.writes("}}\n") |
| return nil |
| } |
| |
| const readMethodsBase = t.IDReadU8 |
| |
| var readMethods = [...]struct { |
| size uint8 |
| n uint8 |
| endianness uint8 |
| }{ |
| t.IDReadU8 - readMethodsBase: {8, 8, 'b'}, |
| |
| t.IDReadU16BE - readMethodsBase: {16, 16, 'b'}, |
| t.IDReadU16LE - readMethodsBase: {16, 16, 'l'}, |
| |
| t.IDReadU8AsU32 - readMethodsBase: {32, 8, 'b'}, |
| t.IDReadU16BEAsU32 - readMethodsBase: {32, 16, 'b'}, |
| t.IDReadU16LEAsU32 - readMethodsBase: {32, 16, 'l'}, |
| t.IDReadU24BEAsU32 - readMethodsBase: {32, 24, 'b'}, |
| t.IDReadU24LEAsU32 - readMethodsBase: {32, 24, 'l'}, |
| t.IDReadU32BE - readMethodsBase: {32, 32, 'b'}, |
| t.IDReadU32LE - readMethodsBase: {32, 32, 'l'}, |
| |
| t.IDReadU8AsU64 - readMethodsBase: {64, 8, 'b'}, |
| t.IDReadU16BEAsU64 - readMethodsBase: {64, 16, 'b'}, |
| t.IDReadU16LEAsU64 - readMethodsBase: {64, 16, 'l'}, |
| t.IDReadU24BEAsU64 - readMethodsBase: {64, 24, 'b'}, |
| t.IDReadU24LEAsU64 - readMethodsBase: {64, 24, 'l'}, |
| t.IDReadU32BEAsU64 - readMethodsBase: {64, 32, 'b'}, |
| t.IDReadU32LEAsU64 - readMethodsBase: {64, 32, 'l'}, |
| t.IDReadU40BEAsU64 - readMethodsBase: {64, 40, 'b'}, |
| t.IDReadU40LEAsU64 - readMethodsBase: {64, 40, 'l'}, |
| t.IDReadU48BEAsU64 - readMethodsBase: {64, 48, 'b'}, |
| t.IDReadU48LEAsU64 - readMethodsBase: {64, 48, 'l'}, |
| t.IDReadU56BEAsU64 - readMethodsBase: {64, 56, 'b'}, |
| t.IDReadU56LEAsU64 - readMethodsBase: {64, 56, 'l'}, |
| t.IDReadU64BE - readMethodsBase: {64, 64, 'b'}, |
| t.IDReadU64LE - readMethodsBase: {64, 64, 'l'}, |
| } |
| |
| const peekMethodsBase = t.IDPeekU8 |
| |
| var peekMethods = [...]struct { |
| size uint8 |
| n uint8 |
| endianness uint8 |
| }{ |
| t.IDPeekU8 - peekMethodsBase: {8, 8, 'b'}, |
| |
| t.IDPeekU16BE - peekMethodsBase: {16, 16, 'b'}, |
| t.IDPeekU16LE - peekMethodsBase: {16, 16, 'l'}, |
| |
| t.IDPeekU8AsU32 - peekMethodsBase: {32, 8, 'b'}, |
| t.IDPeekU16BEAsU32 - peekMethodsBase: {32, 16, 'b'}, |
| t.IDPeekU16LEAsU32 - peekMethodsBase: {32, 16, 'l'}, |
| t.IDPeekU24BEAsU32 - peekMethodsBase: {32, 24, 'b'}, |
| t.IDPeekU24LEAsU32 - peekMethodsBase: {32, 24, 'l'}, |
| t.IDPeekU32BE - peekMethodsBase: {32, 32, 'b'}, |
| t.IDPeekU32LE - peekMethodsBase: {32, 32, 'l'}, |
| |
| t.IDPeekU8AsU64 - peekMethodsBase: {64, 8, 'b'}, |
| t.IDPeekU16BEAsU64 - peekMethodsBase: {64, 16, 'b'}, |
| t.IDPeekU16LEAsU64 - peekMethodsBase: {64, 16, 'l'}, |
| t.IDPeekU24BEAsU64 - peekMethodsBase: {64, 24, 'b'}, |
| t.IDPeekU24LEAsU64 - peekMethodsBase: {64, 24, 'l'}, |
| t.IDPeekU32BEAsU64 - peekMethodsBase: {64, 32, 'b'}, |
| t.IDPeekU32LEAsU64 - peekMethodsBase: {64, 32, 'l'}, |
| t.IDPeekU40BEAsU64 - peekMethodsBase: {64, 40, 'b'}, |
| t.IDPeekU40LEAsU64 - peekMethodsBase: {64, 40, 'l'}, |
| t.IDPeekU48BEAsU64 - peekMethodsBase: {64, 48, 'b'}, |
| t.IDPeekU48LEAsU64 - peekMethodsBase: {64, 48, 'l'}, |
| t.IDPeekU56BEAsU64 - peekMethodsBase: {64, 56, 'b'}, |
| t.IDPeekU56LEAsU64 - peekMethodsBase: {64, 56, 'l'}, |
| t.IDPeekU64BE - peekMethodsBase: {64, 64, 'b'}, |
| t.IDPeekU64LE - peekMethodsBase: {64, 64, 'l'}, |
| } |
| |
| const writeFastMethodsBase = t.IDWriteFastU8 |
| |
| var writeFastMethods = [...]struct { |
| n uint8 |
| endianness uint8 |
| }{ |
| t.IDWriteFastU8 - writeFastMethodsBase: {8, 'b'}, |
| t.IDWriteFastU16BE - writeFastMethodsBase: {16, 'b'}, |
| t.IDWriteFastU16LE - writeFastMethodsBase: {16, 'l'}, |
| t.IDWriteFastU24BE - writeFastMethodsBase: {24, 'b'}, |
| t.IDWriteFastU24LE - writeFastMethodsBase: {24, 'l'}, |
| t.IDWriteFastU32BE - writeFastMethodsBase: {32, 'b'}, |
| t.IDWriteFastU32LE - writeFastMethodsBase: {32, 'l'}, |
| t.IDWriteFastU40BE - writeFastMethodsBase: {40, 'b'}, |
| t.IDWriteFastU40LE - writeFastMethodsBase: {40, 'l'}, |
| t.IDWriteFastU48BE - writeFastMethodsBase: {48, 'b'}, |
| t.IDWriteFastU48LE - writeFastMethodsBase: {48, 'l'}, |
| t.IDWriteFastU56BE - writeFastMethodsBase: {56, 'b'}, |
| t.IDWriteFastU56LE - writeFastMethodsBase: {56, 'l'}, |
| t.IDWriteFastU64BE - writeFastMethodsBase: {64, 'b'}, |
| t.IDWriteFastU64LE - writeFastMethodsBase: {64, 'l'}, |
| } |