可变长度序列,第一位指示序列结束,前子

发布于 2024-10-16 18:08:34 字数 1414 浏览 7 评论 0原文

如何使用 Preon 解析可变长度字节序列,其中第一位 (BigEndian) 指示后面是否有另一个字节?

示例

    byte[] bytecode = new byte[] {
            (byte) 0xf2, (byte) 0xbf, (byte) 0xbf, (byte) 0xbf, (byte) 0x50
    };

注释

  • 最终负载版本中指示下一个被丢弃的第一位
  • 在本文中使用的 Preon

是 1.1 结果字节(十进制)

{ 114, 63, 63, 63, 80 }

已经尝试过

@BoundList + @Choices(有条件)

Limbo exp lang 不支持方法调用,所以你不能检测流结束(前一个块需要有符号 1,当前块需要是最后一个,即符号需要为 0)

使用 @If 的递归方法

public static class Entry {

    @BoundNumber(size = "1", byteOrder = ByteOrder.BigEndian)
    private byte hasNext;

    @BoundNumber(size = "7", byteOrder = ByteOrder.BigEndian)
    private byte payload;

    @If("hasNext > 0")
    @BoundNumber(size = "1", byteOrder = ByteOrder.BigEndian)
    private byte hasNext1;

    @If("hasNext > 0")
    @BoundNumber(size = "7", byteOrder = ByteOrder.BigEndian)
    private byte payload1;

    @If("hasNext1 > 0")
    @BoundObject
    private Entry nextEntry;

    @Override
    public String toString() {
        return hasNext > 0 ? String.valueOf(payload) : String.valueOf(payload)
                + ", " + String.valueOf(payload1);
    }

    //...
}

由于某种原因,例如上面提到的,Preon 将即使应该有 3 个,也只解析 2 个 Entry 实例(父级和子级)。

谢谢。

How would you parse a variable length sequence of bytes where first bit (BigEndian) indicates if another byte is following using Preon?

Example

    byte[] bytecode = new byte[] {
            (byte) 0xf2, (byte) 0xbf, (byte) 0xbf, (byte) 0xbf, (byte) 0x50
    };

Notes

  • first bit that indicates the next is discarded in final payload
  • version of Preon used for this post was 1.1

Result bytes (in decimal)

{ 114, 63, 63, 63, 80 }

Already tried

@BoundList + @Choices(with conditions)

Limbo exp lang doesn't support method calls, so you can't detect end of stream (previous needs to have sign 1 and current block needs to be the last, i.e. sign needs to be 0)

Recursive approach with @If

public static class Entry {

    @BoundNumber(size = "1", byteOrder = ByteOrder.BigEndian)
    private byte hasNext;

    @BoundNumber(size = "7", byteOrder = ByteOrder.BigEndian)
    private byte payload;

    @If("hasNext > 0")
    @BoundNumber(size = "1", byteOrder = ByteOrder.BigEndian)
    private byte hasNext1;

    @If("hasNext > 0")
    @BoundNumber(size = "7", byteOrder = ByteOrder.BigEndian)
    private byte payload1;

    @If("hasNext1 > 0")
    @BoundObject
    private Entry nextEntry;

    @Override
    public String toString() {
        return hasNext > 0 ? String.valueOf(payload) : String.valueOf(payload)
                + ", " + String.valueOf(payload1);
    }

    //...
}

For some reason, for example mentioned above, Preon will only parse 2 instances of Entry (parent and child) even when there should be 3.

Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

为此,我建议根据您想要执行的操作实现您自己的 Codec 或 CodecDecorator。如果您想要做的只是将字节序列存储在您自己的字节数组中,那么创建您自己的编解码器并将其与框架连接起来应该相当容易。

这是编解码器的实现,可能与您正在寻找的内容很接近:

VariableLengthByteArrayCodec:

package org.codehaus.preon.sample.varlength;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import org.codehaus.preon.Builder;
import org.codehaus.preon.Codec;
import org.codehaus.preon.CodecDescriptor;
import org.codehaus.preon.DecodingException;
import org.codehaus.preon.Resolver;
import org.codehaus.preon.buffer.BitBuffer;
import org.codehaus.preon.channel.BitChannel;
import org.codehaus.preon.el.Expression;

import nl.flotsam.pecia.Documenter;
import nl.flotsam.pecia.ParaContents;
import nl.flotsam.pecia.SimpleContents;

public class VariableLengthByteArrayCodec implements Codec<byte[]> {

    public byte[] decode(BitBuffer buffer, Resolver resolver, Builder builder) throws DecodingException {
        ByteArrayOutputStream bout = new ByteArrayOutputStream();
        boolean cont = true;
        while (cont) {
            byte b = buffer.readAsByte(8);
            bout.write(b);
            cont = (b & (1 << 7)) > 0;
        }
        return bout.toByteArray();
    }

    public void encode(byte[] value, BitChannel channel, Resolver resolver) throws IOException {
        channel.write(value, 0, value.length - 1);
    }

    public Expression<Integer, Resolver> getSize() {
        return null;
    }

    public CodecDescriptor getCodecDescriptor() {
        return new CodecDescriptor() {

            public <C extends ParaContents<?>> Documenter<C> summary() {
                return new Documenter<C>() {
                    public void document(C target) {
                        target.document(reference(Adjective.A, true));
                        target.text(".");
                    }
                };
            }

            public <C extends ParaContents<?>> Documenter<C> reference(final Adjective adjective, final boolean startWithCapital) {
                return new Documenter<C>() {
                    public void document(C target) {
                        target.text(adjective.asTextPreferA(startWithCapital))
                                .text("variable length encoded byte array.");
                    }
                };
            }

            public <C extends SimpleContents<?>> Documenter<C> details(String bufferReference) {
                return new Documenter<C>() {
                    public void document(C target) {
                        target.para()
                                .text("The number of bytes is determined by the ")
                                .text("leading bit of the individual bytes; ")
                                .text("if the first bit of a byte is 1, then ")
                                .text("more bytes are expted to follow.");
                    }
                };

            }

            public boolean requiresDedicatedSection() {
                return false;
            }

            public String getTitle() {
                assert requiresDedicatedSection();
                return null;
            }
        };
    }

    public Class<?>[] getTypes() {
        return new Class<?>[] { Byte[].class };
    }

    public Class<?> getType() {
        return Byte[].class;
    }
}

VariableLengthByteArrayCodecFactory:

package org.codehaus.preon.sample.varlength;

import java.lang.reflect.AnnotatedElement;
import org.codehaus.preon.Codec;
import org.codehaus.preon.CodecFactory;
import org.codehaus.preon.ResolverContext;

public class VariableLengthByteArrayCodecFactory implements CodecFactory {

    public <T> Codec<T> create(AnnotatedElement metadata, Class<T> type, ResolverContext context) {
        if (metadata != null && metadata.isAnnotationPresent(VarLengthEncoded.class) && type == byte[].class) {
            return (Codec<T>) new VariableLengthByteArrayCodec();
        } else {
            return null;
        }
    }

}

VarLengthEncoded:

package org.codehaus.preon.sample.varlength;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface VarLengthEncoded {
}

最后,这是如何实现的你使用它:

public static class SomeHolder {

    @VarLengthEncoded byte[] value;

    public byte[] getValue() {
        return value;
    }

}

...
Codec<SomeHolder> codec = Codecs.create(SomeHolder.class, new VariableLengthByteArrayCodecFactory());
SomeHolder holder = Codecs.decode(codec, (byte) 0xff, (byte) 0x0f);
assertThat(holder.getValue(), is(not(nullValue())));
assertThat(holder.getValue().length, is(2));
assertThat(holder.getValue()[0], is((byte) 0xff));
assertThat(holder.getValue()[1], is((byte) 0x0f));

这看起来可能是相当多的代码,但是如果你仔细检查,你会发现大多数代码实际上是确保每当你使用 @VarLengthEncoded 注释为类生成文档时都会生成正确的描述。如果您根本不关心文档,那么您可以简单地返回默认的 CodecDescriptor。

所以,我想,这个答案的本质是:在某些情况下,在 Preon 本身中提供实现肯定会使框架过载。这并不意味着您应该依赖框架默认提供的所有内容。这只是意味着您应该插入自己的扩展。

For this, I would suggest implementing your own Codec or CodecDecorator, depending on what you want to do. If all you want to do is store the sequence of bytes in your own byte array, then creating your own Codec and hooking it up with the framework should be fairly easy.

Here is an implementation of Codec that probably comes close to what you are looking for:

VariableLengthByteArrayCodec:

package org.codehaus.preon.sample.varlength;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import org.codehaus.preon.Builder;
import org.codehaus.preon.Codec;
import org.codehaus.preon.CodecDescriptor;
import org.codehaus.preon.DecodingException;
import org.codehaus.preon.Resolver;
import org.codehaus.preon.buffer.BitBuffer;
import org.codehaus.preon.channel.BitChannel;
import org.codehaus.preon.el.Expression;

import nl.flotsam.pecia.Documenter;
import nl.flotsam.pecia.ParaContents;
import nl.flotsam.pecia.SimpleContents;

public class VariableLengthByteArrayCodec implements Codec<byte[]> {

    public byte[] decode(BitBuffer buffer, Resolver resolver, Builder builder) throws DecodingException {
        ByteArrayOutputStream bout = new ByteArrayOutputStream();
        boolean cont = true;
        while (cont) {
            byte b = buffer.readAsByte(8);
            bout.write(b);
            cont = (b & (1 << 7)) > 0;
        }
        return bout.toByteArray();
    }

    public void encode(byte[] value, BitChannel channel, Resolver resolver) throws IOException {
        channel.write(value, 0, value.length - 1);
    }

    public Expression<Integer, Resolver> getSize() {
        return null;
    }

    public CodecDescriptor getCodecDescriptor() {
        return new CodecDescriptor() {

            public <C extends ParaContents<?>> Documenter<C> summary() {
                return new Documenter<C>() {
                    public void document(C target) {
                        target.document(reference(Adjective.A, true));
                        target.text(".");
                    }
                };
            }

            public <C extends ParaContents<?>> Documenter<C> reference(final Adjective adjective, final boolean startWithCapital) {
                return new Documenter<C>() {
                    public void document(C target) {
                        target.text(adjective.asTextPreferA(startWithCapital))
                                .text("variable length encoded byte array.");
                    }
                };
            }

            public <C extends SimpleContents<?>> Documenter<C> details(String bufferReference) {
                return new Documenter<C>() {
                    public void document(C target) {
                        target.para()
                                .text("The number of bytes is determined by the ")
                                .text("leading bit of the individual bytes; ")
                                .text("if the first bit of a byte is 1, then ")
                                .text("more bytes are expted to follow.");
                    }
                };

            }

            public boolean requiresDedicatedSection() {
                return false;
            }

            public String getTitle() {
                assert requiresDedicatedSection();
                return null;
            }
        };
    }

    public Class<?>[] getTypes() {
        return new Class<?>[] { Byte[].class };
    }

    public Class<?> getType() {
        return Byte[].class;
    }
}

VariableLengthByteArrayCodecFactory:

package org.codehaus.preon.sample.varlength;

import java.lang.reflect.AnnotatedElement;
import org.codehaus.preon.Codec;
import org.codehaus.preon.CodecFactory;
import org.codehaus.preon.ResolverContext;

public class VariableLengthByteArrayCodecFactory implements CodecFactory {

    public <T> Codec<T> create(AnnotatedElement metadata, Class<T> type, ResolverContext context) {
        if (metadata != null && metadata.isAnnotationPresent(VarLengthEncoded.class) && type == byte[].class) {
            return (Codec<T>) new VariableLengthByteArrayCodec();
        } else {
            return null;
        }
    }

}

VarLengthEncoded:

package org.codehaus.preon.sample.varlength;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface VarLengthEncoded {
}

And then finally, this how you use it:

public static class SomeHolder {

    @VarLengthEncoded byte[] value;

    public byte[] getValue() {
        return value;
    }

}

...
Codec<SomeHolder> codec = Codecs.create(SomeHolder.class, new VariableLengthByteArrayCodecFactory());
SomeHolder holder = Codecs.decode(codec, (byte) 0xff, (byte) 0x0f);
assertThat(holder.getValue(), is(not(nullValue())));
assertThat(holder.getValue().length, is(2));
assertThat(holder.getValue()[0], is((byte) 0xff));
assertThat(holder.getValue()[1], is((byte) 0x0f));

That may seem as quite a bit of code, but if you check carefully, then you will notice that most code is actually making sure that there is proper description getting generated whenever you would generate documentation for classes with the @VarLengthEncoded annotation. If you don't care about documentation at all, then you can simply return a default CodecDescriptor.

So, I guess, the essence of this answer is: there are certainly cases in which providing an implementation in Preon itself would overload the framework. That doesn't mean you should rely on everything the framework has to offer by default. It just means you should plug in your own extensions.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文