Skip to content

Commit 3b49f96

Browse files
committed
Fix pack grouped float and x[template] bugs (+438 tests)
- Fixed grouped float formats producing zeros by adding 'f', 'F', 'd', 'D' to isNumericFormat() * Root cause: PackHelper.countValuesNeeded() returned 0 for float formats * Impact: pack('(f)2', 1.5, 2.5) was producing zeros instead of correct values - Fixed x[template] size calculation for complex templates * Implemented calculateTemplateSize() using Pack.pack() for accurate size calculation * Now correctly handles grouped templates, modifiers, and nested structures Test improvement: 13,347 → 13,785 passing tests (+438, 3.3% gain)
1 parent 2fc8750 commit 3b49f96

File tree

2 files changed

+55
-3
lines changed

2 files changed

+55
-3
lines changed

src/main/java/org/perlonjava/operators/pack/PackHelper.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -755,6 +755,7 @@ else if (template.charAt(i) == ')') {
755755
public static boolean isNumericFormat(char format) {
756756
return switch (format) {
757757
case 'c', 'C', 's', 'S', 'l', 'L', 'i', 'I', 'n', 'N', 'v', 'V', 'w', 'W', 'q', 'Q', 'j', 'J',
758+
'f', 'F', 'd', 'D', // Float and double formats
758759
'Z' -> // Z can be used as a count
759760
true;
760761
default -> false;

src/main/java/org/perlonjava/operators/pack/PackParser.java

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -138,9 +138,8 @@ public static ParsedCount parseRepeatCount(String template, int position) {
138138
// Empty brackets - treat as count 0
139139
result.count = 0;
140140
} else {
141-
// Template-based count - for now, use 1 as fallback
142-
result.count = 1;
143-
// TODO: Implement proper template size calculation
141+
// Template-based count - calculate the size of the template
142+
result.count = calculateTemplateSize(countStr);
144143
}
145144

146145
result.endPosition = j;
@@ -329,6 +328,58 @@ private static int countValuesNeeded(String template) {
329328
return Math.max(count, 10); // Ensure we have at least 10 values
330329
}
331330

331+
/**
332+
* Calculates the total size in bytes of a template string.
333+
* For use with x[template] and X[template] formats.
334+
*
335+
* This method uses the actual Pack.pack method to pack dummy data
336+
* and measure the resulting size, which handles all complex cases
337+
* including groups, modifiers, and nested templates correctly.
338+
*
339+
* @param template the template string
340+
* @return the total size in bytes
341+
*/
342+
private static int calculateTemplateSize(String template) {
343+
// Use the actual pack method to calculate the size by packing dummy data
344+
// This handles all complex cases including groups, modifiers, etc.
345+
try {
346+
// Create a RuntimeList with the template and dummy values
347+
org.perlonjava.runtime.RuntimeList args = new org.perlonjava.runtime.RuntimeList();
348+
args.add(new org.perlonjava.runtime.RuntimeScalar(template));
349+
350+
// Count how many values the template needs and add dummy values
351+
int valueCount = countValuesNeeded(template);
352+
for (int j = 0; j < valueCount; j++) {
353+
// Add dummy values - use 0 for numeric formats
354+
args.add(new org.perlonjava.runtime.RuntimeScalar(0));
355+
}
356+
357+
// Pack with the template and measure the result
358+
org.perlonjava.runtime.RuntimeScalar result = org.perlonjava.operators.Pack.pack(args);
359+
return result.toString().length();
360+
} catch (Exception e) {
361+
// If packing fails for any reason, fall back to simple calculation
362+
// This might happen for templates with special requirements
363+
return calculateTemplateSizeSimple(template);
364+
}
365+
}
366+
367+
/**
368+
* Simple fallback calculation for template size.
369+
* This is used when the pack method fails for some reason.
370+
*/
371+
private static int calculateTemplateSizeSimple(String template) {
372+
// For simple fallback, just count format characters and use default sizes
373+
int size = 0;
374+
for (int i = 0; i < template.length(); i++) {
375+
char c = template.charAt(i);
376+
if (Character.isLetter(c) && c != 'X') { // X is backward, doesn't add size
377+
size += getFormatSize(c, false);
378+
}
379+
}
380+
return Math.max(size, 1); // Return at least 1
381+
}
382+
332383
/**
333384
* Returns the size in bytes for a given format character.
334385
*

0 commit comments

Comments
 (0)