Skip to content

Commit a0ce0f7

Browse files
ctf: introduce Dynamic Length String datatype
It's a mix of a sequence and a string. A dynamic-length string field class is an abstract string field class which describes dynamic-length string fields. A static-length string field is a sequence of zero or more contiguous encoded Unicode codepoints. All the encoded codepoints of a dynamic-length string field before the first “NULL” (U+0000) codepoint, if any, form the resulting string value. The first U+0000 codepoint, if any, and all the following bytes are considered padding (garbage data). Change-Id: I19bbef5eb2913d8252e469e477363da7eecacdcb Signed-off-by: Matthew Khouzam <[email protected]>
1 parent b347e64 commit a0ce0f7

File tree

6 files changed

+324
-1
lines changed

6 files changed

+324
-1
lines changed

ctf/org.eclipse.tracecompass.ctf.core/META-INF/MANIFEST.MF

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
22
Bundle-ManifestVersion: 2
33
Bundle-Name: %Bundle-Name
44
Bundle-Vendor: %Bundle-Vendor
5-
Bundle-Version: 5.0.2.qualifier
5+
Bundle-Version: 5.1.0.qualifier
66
Bundle-Localization: plugin
77
Bundle-SymbolicName: org.eclipse.tracecompass.ctf.core;singleton:=true
88
Bundle-ActivationPolicy: lazy
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2025 Ericsson
3+
*
4+
* All rights reserved. This program and the accompanying materials
5+
* are made available under the terms of the Eclipse Public License 2.0
6+
* which accompanies this distribution, and is available at
7+
* https://www.eclipse.org/legal/epl-2.0/
8+
*
9+
* SPDX-License-Identifier: EPL-2.0
10+
*******************************************************************************/
11+
package org.eclipse.tracecompass.ctf.core.event.types;
12+
13+
import org.eclipse.jdt.annotation.NonNull;
14+
import org.eclipse.tracecompass.ctf.core.event.scope.IDefinitionScope;
15+
16+
/**
17+
* A CTF string definition that supports both static and dynamic-length strings.
18+
*
19+
* @author Matthew Khouzam
20+
* @since 5.1
21+
*/
22+
public final class StringDefinition2 extends Definition {
23+
24+
private final String fString;
25+
26+
/**
27+
* Constructor for StringDeclaration
28+
*
29+
* @param declaration the parent declaration
30+
* @param definitionScope the parent scope
31+
* @param fieldName the field name
32+
* @param value the String value
33+
*/
34+
public StringDefinition2(@NonNull StringDeclaration declaration,
35+
IDefinitionScope definitionScope, @NonNull String fieldName, String value) {
36+
super(declaration, definitionScope, fieldName);
37+
fString = value;
38+
}
39+
40+
/**
41+
* Constructor for DynamicLengthStringDeclaration
42+
*
43+
* @param declaration the parent declaration
44+
* @param definitionScope the parent scope
45+
* @param fieldName the field name
46+
* @param value the String value
47+
*/
48+
public StringDefinition2(@NonNull Declaration declaration,
49+
IDefinitionScope definitionScope, @NonNull String fieldName, String value) {
50+
super(declaration, definitionScope, fieldName);
51+
fString = value;
52+
}
53+
54+
/**
55+
* Gets the string value
56+
*
57+
* @return the string
58+
*/
59+
public String getValue() {
60+
return fString;
61+
}
62+
63+
@Override
64+
public long size() {
65+
return fString.length();
66+
}
67+
68+
@Override
69+
public String toString() {
70+
return '\"' + getValue() + '\"';
71+
}
72+
}

ctf/org.eclipse.tracecompass.ctf.core/src/org/eclipse/tracecompass/internal/ctf/core/event/metadata/tsdl/TypeAliasParser.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.eclipse.tracecompass.internal.ctf.core.event.metadata.JsonFieldClassAliasMetadataNode;
2525
import org.eclipse.tracecompass.internal.ctf.core.event.metadata.JsonStructureFieldMemberMetadataNode;
2626
import org.eclipse.tracecompass.internal.ctf.core.event.metadata.ParseException;
27+
import org.eclipse.tracecompass.internal.ctf.core.event.metadata.tsdl.dynamicstring.DynamicLengthStringParser;
2728
import org.eclipse.tracecompass.internal.ctf.core.event.metadata.tsdl.enumeration.EnumParser;
2829
import org.eclipse.tracecompass.internal.ctf.core.event.metadata.tsdl.integer.IntegerDeclarationParser;
2930
import org.eclipse.tracecompass.internal.ctf.core.event.metadata.tsdl.string.StringDeclarationParser;
@@ -159,6 +160,8 @@ public IDeclaration parse(ICTFMetadataNode typealias, ICommonTreeParserParameter
159160
targetDeclaration = VariantParser.INSTANCE.parse(typealias, new VariantParser.Param(trace, scope));
160161
} else if (JsonMetadataStrings.FIXED_UNSIGNED_ENUMERATION.equals(type)) {
161162
targetDeclaration = EnumParser.INSTANCE.parse(typealias, new EnumParser.Param(trace, scope));
163+
} else if (JsonMetadataStrings.DYNAMIC_LENGTH_STRING.equals(type)) {
164+
targetDeclaration = DynamicLengthStringParser.INSTANCE.parse(typealias, new DynamicLengthStringParser.Param(trace));
162165
} else {
163166
throw new ParseException("Invalid field class: " + type); //$NON-NLS-1$
164167
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2025 Ericsson
3+
*
4+
* All rights reserved. This program and the accompanying materials
5+
* are made available under the terms of the Eclipse Public License 2.0
6+
* which accompanies this distribution, and is available at
7+
* https://www.eclipse.org/legal/epl-2.0/
8+
*
9+
* SPDX-License-Identifier: EPL-2.0
10+
*******************************************************************************/
11+
package org.eclipse.tracecompass.internal.ctf.core.event.metadata.tsdl.dynamicstring;
12+
13+
import java.nio.charset.Charset;
14+
import java.nio.charset.StandardCharsets;
15+
16+
import org.eclipse.jdt.annotation.NonNullByDefault;
17+
import org.eclipse.tracecompass.ctf.core.event.types.IDeclaration;
18+
import org.eclipse.tracecompass.ctf.core.trace.CTFTrace;
19+
import org.eclipse.tracecompass.internal.ctf.core.event.metadata.AbstractScopedCommonTreeParser;
20+
import org.eclipse.tracecompass.internal.ctf.core.event.metadata.JsonStructureFieldMemberMetadataNode;
21+
import org.eclipse.tracecompass.internal.ctf.core.event.metadata.ParseException;
22+
import org.eclipse.tracecompass.internal.ctf.core.event.types.DynamicLengthStringDeclaration;
23+
import org.eclipse.tracecompass.internal.ctf.core.event.types.ICTFMetadataNode;
24+
import org.eclipse.tracecompass.internal.ctf.core.utils.JsonMetadataStrings;
25+
26+
import com.google.gson.JsonElement;
27+
import com.google.gson.JsonObject;
28+
29+
/**
30+
* A dynamic-length string field class is an abstract string field class which
31+
* describes dynamic-length string fields.
32+
*
33+
* A dynamic-length string field is a sequence of zero or more contiguous
34+
* encoded Unicode codepoints. All the encoded codepoints of a dynamic-length
35+
* string field before the first "NULL" (U+0000) codepoint, if any, form the
36+
* resulting string value. The first U+0000 codepoint, if any, and all the
37+
* following bytes are considered padding (garbage data).
38+
*
39+
* The length, or number of bytes, of a dynamic-length string field is the
40+
* value of another, anterior (already encoded/decoded) length field. A
41+
* consumer can locate this length field thanks to the length-field-location
42+
* property of the dynamic-length string field class.
43+
*
44+
* @author Matthew Khouzam
45+
*/
46+
public final class DynamicLengthStringParser extends AbstractScopedCommonTreeParser {
47+
48+
/**
49+
* Instance
50+
*/
51+
public static final DynamicLengthStringParser INSTANCE = new DynamicLengthStringParser();
52+
53+
private DynamicLengthStringParser() {
54+
}
55+
56+
@Override
57+
public IDeclaration parse(ICTFMetadataNode node, ICommonTreeParserParameter param) throws ParseException {
58+
if (!(node instanceof JsonStructureFieldMemberMetadataNode)) {
59+
throw new ParseException("Dynamic-length string only supported in JSON metadata"); //$NON-NLS-1$
60+
}
61+
62+
JsonStructureFieldMemberMetadataNode member = (JsonStructureFieldMemberMetadataNode) node;
63+
JsonObject fieldClass = member.getFieldClass().getAsJsonObject();
64+
65+
JsonElement lengthFieldLocation = fieldClass.get(JsonMetadataStrings.LENGTH_FIELD_LOCATION);
66+
if (lengthFieldLocation == null) {
67+
throw new ParseException("Dynamic-length string requires length-field-location property"); //$NON-NLS-1$
68+
}
69+
JsonElement encodingField = fieldClass.get(JsonMetadataStrings.ENCODING);
70+
String lengthName = lengthFieldLocation.getAsJsonObject().get(JsonMetadataStrings.PATH).getAsString();
71+
Charset encoding = encodingField != null ?
72+
JsonMetadataStrings.ENCODINGS.getOrDefault(encodingField.getAsString(), StandardCharsets.UTF_8) :
73+
StandardCharsets.UTF_8;
74+
return new DynamicLengthStringDeclaration(lengthName, encoding);
75+
}
76+
77+
/**
78+
* Parameters for the dynamic-length string parser
79+
*/
80+
@NonNullByDefault
81+
public static final class Param implements ICommonTreeParserParameter {
82+
private final CTFTrace fTrace;
83+
84+
/**
85+
* Parameter constructor
86+
*
87+
* @param trace the trace
88+
*/
89+
public Param(CTFTrace trace) {
90+
fTrace = trace;
91+
}
92+
93+
/**
94+
* Get the trace
95+
*
96+
* @return the trace
97+
*/
98+
public CTFTrace getTrace() {
99+
return fTrace;
100+
}
101+
}
102+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2025 Ericsson
3+
*
4+
* All rights reserved. This program and the accompanying materials
5+
* are made available under the terms of the Eclipse Public License 2.0
6+
* which accompanies this distribution, and is available at
7+
* https://www.eclipse.org/legal/epl-2.0/
8+
*
9+
* SPDX-License-Identifier: EPL-2.0
10+
*******************************************************************************/
11+
package org.eclipse.tracecompass.internal.ctf.core.event.types;
12+
13+
import java.nio.charset.Charset;
14+
15+
import org.eclipse.jdt.annotation.Nullable;
16+
import org.eclipse.tracecompass.ctf.core.CTFException;
17+
import org.eclipse.tracecompass.ctf.core.event.io.BitBuffer;
18+
import org.eclipse.tracecompass.ctf.core.event.scope.IDefinitionScope;
19+
import org.eclipse.tracecompass.ctf.core.event.types.Declaration;
20+
import org.eclipse.tracecompass.ctf.core.event.types.IDeclaration;
21+
import org.eclipse.tracecompass.ctf.core.event.types.IDefinition;
22+
import org.eclipse.tracecompass.ctf.core.event.types.IntegerDefinition;
23+
import org.eclipse.tracecompass.ctf.core.event.types.StringDefinition2;
24+
25+
/**
26+
* Dynamic-length string declaration with encoding support
27+
*
28+
* @author Matthew Khouzam
29+
*/
30+
public class DynamicLengthStringDeclaration extends Declaration {
31+
32+
private final Charset fEncoding;
33+
private final String fLengthName;
34+
35+
/**
36+
* Constructor
37+
*
38+
* @param lengthName the name of the length field
39+
* @param encoding the character encoding
40+
*/
41+
public DynamicLengthStringDeclaration(@Nullable String lengthName, Charset encoding) {
42+
fLengthName = lengthName;
43+
fEncoding = encoding;
44+
}
45+
46+
/**
47+
* Get the encoding
48+
*
49+
* @return the character encoding
50+
*/
51+
public Charset getEncoding() {
52+
return fEncoding;
53+
}
54+
55+
/**
56+
* Get the length field name
57+
*
58+
* @return the length field name
59+
*/
60+
public String getLengthName() {
61+
return fLengthName;
62+
}
63+
64+
@Override
65+
public StringDefinition2 createDefinition(@Nullable IDefinitionScope definitionScope, String fieldName, BitBuffer input) throws CTFException {
66+
IDefinition lenDef = null;
67+
if (definitionScope != null) {
68+
lenDef = definitionScope.lookupDefinition(fLengthName);
69+
}
70+
if (lenDef == null) {
71+
throw new CTFException("Length field not found: " + fLengthName); //$NON-NLS-1$
72+
}
73+
if (!(lenDef instanceof IntegerDefinition)) {
74+
throw new CTFException("Length field must be an integer"); //$NON-NLS-1$
75+
}
76+
long rawLength = ((IntegerDefinition) lenDef).getValue();
77+
if (rawLength < 0) {
78+
throw new CTFException("Cannot have a length < 0, declared = " + rawLength); //$NON-NLS-1$
79+
}
80+
if (rawLength > 1e6) {
81+
throw new CTFException("Cannot have a length > 1000000, declared = " + rawLength); //$NON-NLS-1$
82+
}
83+
int length = (int)rawLength;
84+
byte[] bytes = new byte[length];
85+
for (int i = 0; i < length; i++) {
86+
bytes[i] = (byte) input.get(Byte.SIZE, false);
87+
}
88+
String value = new String(bytes, fEncoding);
89+
int nullIndex = value.indexOf('\0');
90+
if (nullIndex >= 0) {
91+
value = value.substring(0, nullIndex);
92+
}
93+
return new StringDefinition2(this, definitionScope, fieldName, value);
94+
}
95+
96+
@Override
97+
public long getAlignment() {
98+
return Byte.SIZE;
99+
}
100+
101+
@Override
102+
public int getMaximumSize() {
103+
return Integer.MAX_VALUE;
104+
}
105+
106+
@Override
107+
public String toString() {
108+
return "dynamic_string[" + fLengthName + "]<" + fEncoding.name() + ">"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
109+
}
110+
111+
@Override
112+
public boolean isBinaryEquivalent(IDeclaration other) {
113+
// TODO Auto-generated method stub
114+
return false;
115+
}
116+
}

ctf/org.eclipse.tracecompass.ctf.core/src/org/eclipse/tracecompass/internal/ctf/core/utils/JsonMetadataStrings.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@
1313

1414
package org.eclipse.tracecompass.internal.ctf.core.utils;
1515

16+
import java.nio.charset.Charset;
17+
import java.nio.charset.StandardCharsets;
18+
import java.util.Map;
19+
1620
/**
1721
* Various strings for CTF2 implementation.
1822
*
@@ -163,6 +167,20 @@ private JsonMetadataStrings() {
163167
*/
164168
public static final String NULL_TERMINATED_STRING = "null-terminated-string"; //$NON-NLS-1$
165169

170+
/**
171+
* The length of a dynamic string
172+
*/
173+
public static final String LENGTH = "length"; //$NON-NLS-1$
174+
175+
/**
176+
* Type string for a null terminated string field class
177+
*/
178+
public static final String DYNAMIC_LENGTH_STRING = "dynamic-length-string"; //$NON-NLS-1$
179+
180+
/**
181+
* The length field location for a dynamic string
182+
*/
183+
public static final String LENGTH_FIELD_LOCATION = "length-field-location"; //$NON-NLS-1$
166184
/**
167185
* Type string for a fixed length unsigned enumeration field class
168186
*/
@@ -182,4 +200,16 @@ private JsonMetadataStrings() {
182200
* Type string for a structure field class
183201
*/
184202
public static final String STRUCTURE = "structure"; //$NON-NLS-1$
203+
204+
public static final Map<String, Charset> ENCODINGS = Map.of("utf-8",StandardCharsets.UTF_8, //$NON-NLS-1$
205+
"utf-16be",StandardCharsets.UTF_16BE,"utf-16le",StandardCharsets.UTF_16LE, //$NON-NLS-1$ //$NON-NLS-2$
206+
"utf-32be", Charset.forName("UTF-32BE"),"utf-32le", Charset.forName("UTF-32LE")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
207+
208+
/**
209+
* Type of string encoding
210+
*/
211+
public static final String ENCODING = "encoding"; //$NON-NLS-1$
212+
213+
214+
185215
}

0 commit comments

Comments
 (0)