|
5 | 5 | import org.perlonjava.runtime.*;
|
6 | 6 | import org.perlonjava.operators.FormatModifierValidator;
|
7 | 7 |
|
| 8 | +import java.math.BigInteger; |
8 | 9 | import java.util.*;
|
9 | 10 |
|
10 | 11 | import static org.perlonjava.runtime.RuntimeScalarCache.scalarUndef;
|
@@ -314,36 +315,79 @@ public static RuntimeList unpack(int ctx, RuntimeBase... args) {
|
314 | 315 | checksumHandler.unpack(state, tempValues, count, isStarCount);
|
315 | 316 | }
|
316 | 317 |
|
317 |
| - // Calculate checksum based on format (accumulate without masking like Perl) |
318 |
| - long checksum = 0; |
319 |
| - if (format == 'b' || format == 'B') { |
320 |
| - // For binary formats, count 1 bits |
321 |
| - for (RuntimeBase value : tempValues) { |
322 |
| - String binary = value.toString(); |
323 |
| - for (int j = 0; j < binary.length(); j++) { |
324 |
| - if (binary.charAt(j) == '1') { |
325 |
| - checksum++; |
| 318 | + // Calculate checksum - use BigInteger for 64+ bit checksums to handle unsigned values |
| 319 | + if (checksumBits >= 64) { |
| 320 | + // Use BigInteger for 64+ bit checksums to handle unsigned values correctly |
| 321 | + BigInteger bigChecksum = BigInteger.ZERO; |
| 322 | + |
| 323 | + if (format == 'b' || format == 'B') { |
| 324 | + // For binary formats, count 1 bits |
| 325 | + for (RuntimeBase value : tempValues) { |
| 326 | + String binary = value.toString(); |
| 327 | + for (int j = 0; j < binary.length(); j++) { |
| 328 | + if (binary.charAt(j) == '1') { |
| 329 | + bigChecksum = bigChecksum.add(BigInteger.ONE); |
| 330 | + } |
326 | 331 | }
|
327 | 332 | }
|
| 333 | + } else { |
| 334 | + // For other formats, sum the numeric values |
| 335 | + for (RuntimeBase value : tempValues) { |
| 336 | + long val = ((RuntimeScalar) value).getLong(); |
| 337 | + // Handle negative values as unsigned |
| 338 | + if (val < 0) { |
| 339 | + bigChecksum = bigChecksum.add(BigInteger.valueOf(val).add(BigInteger.ONE.shiftLeft(64))); |
| 340 | + } else { |
| 341 | + bigChecksum = bigChecksum.add(BigInteger.valueOf(val)); |
| 342 | + } |
| 343 | + } |
| 344 | + } |
| 345 | + |
| 346 | + // Apply bit mask for 64-bit (not for 65+ as they overflow anyway) |
| 347 | + if (checksumBits == 64) { |
| 348 | + // 64-bit mask: 2^64 - 1 = 0xFFFFFFFFFFFFFFFF |
| 349 | + BigInteger mask = BigInteger.ONE.shiftLeft(64).subtract(BigInteger.ONE); |
| 350 | + bigChecksum = bigChecksum.and(mask); |
328 | 351 | }
|
| 352 | + // For checksumBits > 64, Perl treats it as no mask (full value) |
| 353 | + |
| 354 | + // Convert BigInteger to long - this will wrap around for unsigned values > Long.MAX_VALUE |
| 355 | + // which matches Perl's behavior on 64-bit systems |
| 356 | + values.add(new RuntimeScalar(bigChecksum.longValue())); |
329 | 357 | } else {
|
330 |
| - // For other formats, sum the numeric values |
331 |
| - for (RuntimeBase value : tempValues) { |
332 |
| - checksum += ((RuntimeScalar) value).getLong(); |
| 358 | + // Use long for checksums < 64 bits |
| 359 | + long checksum = 0; |
| 360 | + if (format == 'b' || format == 'B') { |
| 361 | + // For binary formats, count 1 bits |
| 362 | + for (RuntimeBase value : tempValues) { |
| 363 | + String binary = value.toString(); |
| 364 | + for (int j = 0; j < binary.length(); j++) { |
| 365 | + if (binary.charAt(j) == '1') { |
| 366 | + checksum++; |
| 367 | + } |
| 368 | + } |
| 369 | + } |
| 370 | + } else { |
| 371 | + // For other formats, sum the numeric values |
| 372 | + for (RuntimeBase value : tempValues) { |
| 373 | + checksum += ((RuntimeScalar) value).getLong(); |
| 374 | + } |
333 | 375 | }
|
334 |
| - } |
335 | 376 |
|
336 |
| - // Apply bit mask at the very end like Perl (not during accumulation) |
337 |
| - if (checksumBits > 0 && checksumBits < 64) { |
338 |
| - long mask = (1L << checksumBits) - 1; |
339 |
| - checksum &= mask; |
340 |
| - } |
| 377 | + // Apply bit mask at the very end like Perl (not during accumulation) |
| 378 | + if (checksumBits > 0 && checksumBits < 64) { |
| 379 | + long mask = (1L << checksumBits) - 1; |
| 380 | + checksum &= mask; |
| 381 | + } |
341 | 382 |
|
342 |
| - // For 32-bit checksums that would be negative as int, convert to long |
343 |
| - if (checksumBits == 32 && checksum > Integer.MAX_VALUE) { |
344 |
| - values.add(new RuntimeScalar(checksum)); |
345 |
| - } else { |
346 |
| - values.add(new RuntimeScalar((int) checksum)); |
| 383 | + // Return the checksum value: |
| 384 | + // - For checksums that fit in an int, return as int |
| 385 | + // - For larger values or checksums with more than 32 bits, return as long |
| 386 | + if (checksumBits > 32 || checksum > Integer.MAX_VALUE || checksum < Integer.MIN_VALUE) { |
| 387 | + values.add(new RuntimeScalar(checksum)); |
| 388 | + } else { |
| 389 | + values.add(new RuntimeScalar((int) checksum)); |
| 390 | + } |
347 | 391 | }
|
348 | 392 | }
|
349 | 393 | } else {
|
|
0 commit comments