Skip to content

Add ability to report positions relative to root stream and add PositionInfo interface #28

New issue

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

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

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions src/main/java/io/kaitai/struct/ArraySpan.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/**
* Copyright 2015-2021 Kaitai Project: MIT license
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

package io.kaitai.struct;

import java.util.ArrayList;
import java.util.List;

/**
* Span that represents positional information of array field and each of it
* elements. Spans of items is available in the {@link #items} field.
*
* @since 0.10
*/
public class ArraySpan extends Span {
/** Individual span of the each item in the array. */
public final List<Span> items;

/**
* Creates a span of array that starts at the current stream offset and
* ends at the unknown position.
*
* @param io the stream to get positional information
*/
public ArraySpan(KaitaiStream io) {
super(io);
items = new ArrayList<Span>();
}

public ArraySpan(KaitaiStream io, int size) {
super(io);
items = new ArrayList<Span>(size);
}

/**
* Appends a new span of array item from current stream position to the end-of-stream
* to this span
*
* @param io Stream used to inquire current position
* @return A new span, added to the internal list of item spans
*/
public Span addItem(KaitaiStream io) {
final Span span = new Span(io);
items.add(span);
return span;
}
}
31 changes: 29 additions & 2 deletions src/main/java/io/kaitai/struct/ByteBufferKaitaiStream.java
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,20 @@ public ByteBufferKaitaiStream(String fileName) throws IOException {
* @param arr byte array to read from or write to
*/
public ByteBufferKaitaiStream(byte[] arr) {
this(arr, 0);
}

/**
* Initializes a stream that will get data from the given byte array when read.
* Internally, ByteBuffer wrapping given array will be used.
*
* @param arr byte array to read
* @param offset offset from the root stream where this stream begins
*
* @since 0.11
*/
public ByteBufferKaitaiStream(byte[] arr, long offset) {
super(offset);
fc = null;
bb = ByteBuffer.wrap(arr);
}
Expand All @@ -73,6 +87,19 @@ public ByteBufferKaitaiStream(byte[] arr) {
* @param buffer {@link ByteBuffer} to read from or write to
*/
public ByteBufferKaitaiStream(ByteBuffer buffer) {
this(buffer, 0);
}

/**
* Initializes a stream that will get data from given {@link ByteBuffer} on read.
*
* @param buffer ByteBuffer to read from
* @param offset offset from the root stream where this stream begins
*
* @since 0.11
*/
public ByteBufferKaitaiStream(ByteBuffer buffer, long offset) {
super(offset);
fc = null;
bb = buffer;
}
Expand Down Expand Up @@ -221,8 +248,8 @@ public void seek(long newPos) {
}

@Override
public int pos() {
return bb.position() + ((bitsWriteMode && bitsLeft > 0) ? 1 : 0);
public long pos() {
Copy link
Member

@generalmimon generalmimon Nov 13, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand that you want to add support for >2GB offsets for RandomAccessFileKaitaiStream, but I'm afraid it's not that simple, it deserves more careful consideration. The problem is that long is not automatically coercible to int, and int is the target type for CalcIntType in KS. CalcIntType is the default integer type for all expressions - in fact, almost any integer value instance will have that type.

Switching _io.pos type to u8 (long) requires additional changes in KSC - whenever you use _io.pos in expression, the derived type must be long, not int. The same applies to _io.size - the fact that it's declared as long is a bit heedless. Right now, when you reference _io.pos in a value instance like this:

instances:
  v:
    value: _io.size

despite the _io.size is declared as

it will be truncated to int anyway:

    public Integer v() {
        if (this.v != null)
            return this.v;
        int _tmp = (int) (_io().size());
        this.v = _tmp;
        return this.v;
    }

Another solution would be switch target type of CalcIntType from int to long (see kaitai-io/kaitai_struct#510), but it also requires a careful consideration - as I understand, long cannot be used everywhere in Java, for example in array indices. Morever, Java conventions suggest to use int whenever possible, and it should be probably also more efficient at least on 32-bit platforms.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm. All that you say is applicable to the size, but pos, it seems, is not used in the array subscription expressions... At least none of the https://github.com/kaitai-io/kaitai_struct_tests fail, but I'm not known if these are ALL tests...

Also, it seems that pos() isn't used much in the computations: if you try to find usages of the pos in the https://github.com/kaitai-io/kaitai_struct_formats repo (RegExp: pos[^:_a-z]) you'll find only 16 occurrences that used only with conjunction with the size (which is already long) or plain number. So at least that change seems to not break anything.

Results from VS Code
Результаты: 16 - Файлы 12

kaitai_struct • formats\archive\gzip.ksy:
  64    - id: body
  65:     size: _io.size - _io.pos - 8
  66      doc: |

kaitai_struct • formats\archive\phar_without_stub.ksy:
  300        - id: data
  301:         size: _io.size - _io.pos - 8
  302          doc: |

kaitai_struct • formats\database\tsm.ksy:
  38          repeat: until
  39:         repeat-until: _io.pos == _io.size - 8
  40          type: index_header

kaitai_struct • formats\filesystem\vdi.ksy:
  159              type: geometry
  160:             if: _parent.version.major>=1 and _io.pos + 16 <= _io.size
  161  

kaitai_struct • formats\hardware\mifare\mifare_classic.ksy:
  33          -orig-id: abtData
  34:         size: _io.size - _io.pos - 16 # sizeof(trailer)
  35          type: filler

kaitai_struct • formats\log\systemd_journal.ksy:
  134        - id: padding
  135:         size: (8 - _io.pos) % 8
  136        - id: object_type

kaitai_struct • formats\media\android_opengl_shaders_cache.ksy:
  25        - id: alignment
  26:         size: "(_io.pos + 3) & ~3 - _io.pos"
  27          doc: garbage from memory

kaitai_struct • formats\media\blender_blend.ksy:
  114        - id: padding_1
  115:         size: (4 - _io.pos) % 4
  116  

  128        - id: padding_2
  129:         size: (4 - _io.pos) % 4
  130  

  139        - id: padding_3
  140:         size: (4 - _io.pos) % 4
  141  

kaitai_struct • formats\media\id3v2_3.ksy:
  26          repeat: until
  27:         repeat-until: _io.pos + _.size > header.size.value or _.is_invalid
  28        - id: padding

kaitai_struct • formats\media\id3v2_4.ksy:
  27          repeat: until
  28:         repeat-until: _io.pos + _.size.value > header.size.value or _.is_invalid
  29        - id: padding

kaitai_struct • formats\network\rtp_packet.ksy:
  37    - id: data
  38:     size: _io.size - _io.pos - len_padding
  39      doc: Payload without padding.

kaitai_struct • formats\windows\windows_resource_file.ksy:
  63        - id: padding1
  64:         size: (4 - _io.pos) % 4
  65        - id: format_version

  84        - id: padding2
  85:         size: (4 - _io.pos) % 4
  86      instances:

So, if you conclusion about pos() type wouldn't changed, just say and I'll revert this

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@generalmimon, could you comment on this?

return bb.position() + ((bitsWriteMode && bitsLeft > 0) ? 1L : 0L);
}

@Override
Expand Down
30 changes: 29 additions & 1 deletion src/main/java/io/kaitai/struct/KaitaiStream.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,31 @@
public abstract class KaitaiStream implements Closeable {
protected int bitsLeft = 0;
protected long bits = 0;
/**
* Offset from the root stream where this stream begins.
*
* @since 0.11
*/
protected final long offset;
protected boolean bitsLe = false;
protected boolean bitsWriteMode = false;

protected WriteBackHandler writeBackHandler;

protected List<KaitaiStream> childStreams = new ArrayList<>();

/** Initializes a stream with zero offset from the root stream. */
public KaitaiStream() { this(0); }

/**
* Initializes a stream with specified offset from the root stream.
*
* @param offset offset from the root stream where this stream begins
*
* @since 0.11
*/
public KaitaiStream(long offset) { this.offset = offset; }

@Override
abstract public void close() throws IOException;

Expand All @@ -90,11 +108,21 @@ public abstract class KaitaiStream implements Closeable {
*/
abstract public void seek(long newPos);

/**
* Get position of a stream pointer relative to the root stream in the stream hierarchy.
* Root stream is a stream without parent stream.
*
* @return the pointer position, number of bytes from the beginning of the root stream
*
* @since 0.11
*/
public long offset() { return this.offset; }

/**
* Get current position of a stream pointer.
* @return pointer position, number of bytes from the beginning of the stream
*/
abstract public int pos();
abstract public long pos();

/**
* Get total size of the stream in bytes.
Expand Down
55 changes: 55 additions & 0 deletions src/main/java/io/kaitai/struct/PositionInfo.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/**
* Copyright 2015-2021 Kaitai Project: MIT license
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

package io.kaitai.struct;

import java.util.Map;

/**
* This interface is implemented by each {@link KaitaiStruct} successor, if
* class was generated with positional information.
* <p>
* If you want to work with generated structures in the generic way, you can use
* following code snipped to deal with positions information:
* <code>
* final KaitaiStruct struct = ...;
* // Generator generates classes, that implements this interface,
* // if debug mode/positions-info is enabled
* if (struct instanceof PositionInfo) {
* final PositionInfo info = (PositionInfo)struct;
* //...
* }
* </code>
*
* @since 0.10
*/
public interface PositionInfo {
/**
* Information about each struct field. If field is an array, then corresponding
* {@code Span} will be of {@link ArraySpan} instance. Map keys is equals to the
* names of the java methods/fields in the generated class.
*
* @return the map from field name to field span information.
*/
Map<String, Span> _spans();
}
Original file line number Diff line number Diff line change
Expand Up @@ -111,10 +111,9 @@ public void seek(long newPos) {
}

@Override
public int pos() {
public long pos() {
try {
// FIXME cast
return (int) raf.getFilePointer() + ((bitsWriteMode && bitsLeft > 0) ? 1 : 0);
return raf.getFilePointer() + ((bitsWriteMode && bitsLeft > 0) ? 1L : 0L);
} catch (IOException e) {
throw new RuntimeException(e);
}
Expand Down
88 changes: 88 additions & 0 deletions src/main/java/io/kaitai/struct/Span.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/**
* Copyright 2015-2021 Kaitai Project: MIT license
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

package io.kaitai.struct;

/**
* Information about positions of parsed value in the streams.
*
* @since 0.10
*/
public class Span {
/** Offset from begin of the root stream, for which that span was created. */
public final long offset;
/**
* Offset from begin of the stream, from which value was parsed. This is relative
* position, to get an absolute position (relative to the root stream) use
* {@link #absoluteStart()}. That offset is always non-negative.
*/
public final long start;
/**
* Offset from begin of the stream, from which value was parsed. This is relative
* position, to get an absolute position (relative to the root stream) use
* {@link #absoluteEnd()}.
* <p>
* If that offset is negative, then value wasn't parsed yet or exception was
* thrown while parsing value.
*/
public long end = -1;

/**
* Creates a span that starts at the current stream offset and ends at
* the unknown position.
*
* @param io the stream to get the positional information
*/
public Span(KaitaiStream io) {
this(io.offset(), io.pos());
}
private Span(long offset, long start) {
this.offset = offset;
this.start = start;
}

/**
* Offset to the start of this span relative to the root stream.
*
* @return start offset from the root stream
*/
public long absoluteStart() { return offset + start; }
/**
* Offset to the end of this span relative to the root stream.
* <p>
* If that offset is negative, then value wasn't parsed yet or exception was
* thrown while parsing value.
*
* @return start offset from the root stream or negative value if value not yet parsed
*/
public long absoluteEnd() { return end < 0 ? -1 : offset + end; }
/**
* Size of this span in bytes.
* <p>
* If size is negative, then value wasn't parsed yet or exception was
* thrown while parsing value.
*
* @return size of the span in bytes
*/
public long size() { return end < 0 ? -1 : end - start; }
}