Skip to content

OftDecoder doesn't provide the name of the type on the typeToken when encoding is nested in a composite #477

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Closed
mikeb01 opened this issue Jun 29, 2017 · 5 comments

Comments

@mikeb01
Copy link
Contributor

mikeb01 commented Jun 29, 2017

If I have a type nested within a composite, when I arrive at the onEncoding callback the typeToken doesn't reference the type as described in the type parameter of the ref element: <ref name="otherInstructionId" type="Fix24CharString"/>.

Using the supplied code I get the following output. When we arrive at the instructionId field on the message the typeToken contains the name of the correct type (Fix24CharString), where as we we arrive at orderState.otherInstructionId, the typeToken contains the name of the ref (otherInstructionId), where as I would expect either the name or referencedName to contain the name of the type:

instructionId
fieldToken: Token{signal=BEGIN_FIELD, name='instructionId', referencedName='null', description='null', id=4, version=0, deprecated=0, encodedLength=0, offset=0, componentTokenCount=3, encoding=Encoding{presence=REQUIRED, primitiveType=null, byteOrder=LITTLE_ENDIAN, minValue=null, maxValue=null, nullValue=null, constValue=null, characterEncoding='null', epoch='unix', timeUnit=nanosecond, semanticType='null'}}
typeToken : Token{signal=ENCODING, name='Fix24CharString', referencedName='null', description='null', id=-1, version=0, deprecated=0, encodedLength=24, offset=0, componentTokenCount=1, encoding=Encoding{presence=REQUIRED, primitiveType=CHAR, byteOrder=LITTLE_ENDIAN, minValue=null, maxValue=null, nullValue=null, constValue=null, characterEncoding='US-ASCII', epoch='unix', timeUnit=nanosecond, semanticType='null'}}
orderState.otherInstructionId
fieldToken: Token{signal=ENCODING, name='otherInstructionId', referencedName='null', description='null', id=-1, version=0, deprecated=0, encodedLength=24, offset=0, componentTokenCount=1, encoding=Encoding{presence=REQUIRED, primitiveType=CHAR, byteOrder=LITTLE_ENDIAN, minValue=null, maxValue=null, nullValue=null, constValue=null, characterEncoding='US-ASCII', epoch='null', timeUnit=null, semanticType='null'}}
typeToken : Token{signal=ENCODING, name='otherInstructionId', referencedName='null', description='null', id=-1, version=0, deprecated=0, encodedLength=24, offset=0, componentTokenCount=1, encoding=Encoding{presence=REQUIRED, primitiveType=CHAR, byteOrder=LITTLE_ENDIAN, minValue=null, maxValue=null, nullValue=null, constValue=null, characterEncoding='US-ASCII', epoch='null', timeUnit=null, semanticType='null'}}

Schema:

<?xml version="1.0" encoding="UTF-8"?>
<sbe:messageSchema
        xmlns:sbe="http://fixprotocol.io/2016/sbe"
        package="sbeir.bug"
        id="10001"
        version="209"
        description="Order Book Events persistent structure"
        byteOrder="littleEndian">

    <types>
        <composite name="messageHeader" description="Message identifiers and length of message root">
            <type name="blockLength" primitiveType="uint16"/>
            <type name="templateId" primitiveType="uint16"/>
            <type name="schemaId" primitiveType="uint16"/>
            <type name="version" primitiveType="uint16"/>
        </composite>
        <type name="Fix24CharString" primitiveType="char" length="24"/>
        <composite name="EvOrderState">
            <ref name="otherInstructionId" type="Fix24CharString"/>
        </composite>
    </types>

    <sbe:message name="ExecutionReport" id="3">
        <field name="instructionId" id="4" type="Fix24CharString"/>
        <field name="orderState" id="5" type="EvOrderState"/>
    </sbe:message>

</sbe:messageSchema>

Code:

import org.agrona.DirectBuffer;
import org.agrona.concurrent.UnsafeBuffer;
import org.junit.Test;
import sbeir.bug.ExecutionReportEncoder;
import sbeir.bug.MessageHeaderEncoder;
import uk.co.real_logic.sbe.ir.Ir;
import uk.co.real_logic.sbe.ir.IrDecoder;
import uk.co.real_logic.sbe.ir.Token;
import uk.co.real_logic.sbe.otf.AbstractTokenListener;
import uk.co.real_logic.sbe.otf.OtfHeaderDecoder;
import uk.co.real_logic.sbe.otf.OtfMessageDecoder;

import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.StandardOpenOption;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.List;

import static java.util.stream.Collectors.joining;

public class Bug
{
    @Test
    public void test() throws IOException
    {
        UnsafeBuffer buffer = new UnsafeBuffer(new byte[4096]);
        MessageHeaderEncoder headerEncoder = new MessageHeaderEncoder();
        ExecutionReportEncoder executionReportEncoder = new ExecutionReportEncoder();

        executionReportEncoder.wrapAndApplyHeader(buffer, 0, headerEncoder);
        executionReportEncoder.instructionId("abcdef");
        executionReportEncoder.orderState().otherInstructionId("ghijk");

        final ByteBuffer byteBuffer = readOrderBookEventsIr("/bug.sbeir");

        Ir orderBookEventsIr = new IrDecoder(byteBuffer).decode();
        OtfHeaderDecoder headerDecoder = new OtfHeaderDecoder(orderBookEventsIr.headerStructure());

        final int blockLength = headerDecoder.getBlockLength(buffer, 0);
        final int templateId = headerDecoder.getTemplateId(buffer, 0);

        OtfMessageDecoder.decode(
            buffer,
            headerDecoder.encodedLength(),
            209,
            blockLength,
            orderBookEventsIr.getMessage(templateId),
            new AbstractTokenListener()
            {
                private final Deque<String> path = new ArrayDeque<>();
                private int compositeDepth = 0;

                @Override
                public void onBeginComposite(final Token fieldToken, final List<Token> tokens, final int fromIndex, final int toIndex)
                {
                    if (compositeDepth > 0)
                    {
                        path.addLast(tokens.get(fromIndex).name());
                    }
                    else
                    {
                        path.addLast(fieldToken.name());
                    }
                    compositeDepth++;
                }

                @Override
                public void onEndComposite(final Token fieldToken, final List<Token> tokens, final int fromIndex, final int toIndex)
                {
                    path.removeLast();
                    compositeDepth--;
                }

                @Override
                public void onEncoding(final Token fieldToken, final DirectBuffer buffer, final int bufferIndex, final Token typeToken, final int actingVersion)
                {
                    path.addLast(fieldToken.name());
                    System.out.printf("%s%n", path.stream().collect(joining(".")));
                    System.out.printf("fieldToken: %s%n", fieldToken);
                    System.out.printf("typeToken : %s%n", typeToken);
                    path.removeLast();
                }
            });
    }

    private ByteBuffer readOrderBookEventsIr(String s) throws IOException
    {
        FileChannel open = FileChannel.open(new File("bug.sbeir").toPath(), StandardOpenOption.READ);
        return open.map(FileChannel.MapMode.READ_ONLY, 0, open.size());
    }
}
mjpt777 added a commit that referenced this issue Jun 29, 2017
@mjpt777
Copy link
Contributor

mjpt777 commented Jun 29, 2017

I missed the case on a simple type. Does this work now for you?

@mikeb01
Copy link
Contributor Author

mikeb01 commented Jun 30, 2017

This works. Another observation. The fieldToken supplied when in the onEncoding callback is not a token representing the field, but matches the typeToken. To be consistent with the java doc, I would of expected it to be the token for the message field, so should have the name orderState instead of originalInstructionId which is the name of the ref within the composite.

@mjpt777
Copy link
Contributor

mjpt777 commented Jun 30, 2017

This is the fair point. With the extension of composites from RC2 this has evolved and show be the field or containing containing composite.

@mjpt777
Copy link
Contributor

mjpt777 commented Jun 30, 2017

Note that composites can be nested to any depth.

@mikeb01
Copy link
Contributor Author

mikeb01 commented Jul 1, 2017

May as well close this now, I have some more questions on the OtfDecoder, but I can follow up in gitter.

@mikeb01 mikeb01 closed this as completed Jul 1, 2017
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants