From e729900072c9ace02473572879f29c0a8e9c5db0 Mon Sep 17 00:00:00 2001 From: Yutaro Sakamoto Date: Mon, 27 Nov 2023 16:53:05 +0900 Subject: [PATCH 1/3] [Feat]: implement table-sort --- .../libcobj/data/CobolDataStorage.java | 5 + .../libcobj/file/CobolFileSort.java | 159 ++++++++++++++++++ tests/run.src/miscellaneous.at | 1 - 3 files changed, 164 insertions(+), 1 deletion(-) diff --git a/libcobj/app/src/main/java/jp/osscons/opensourcecobol/libcobj/data/CobolDataStorage.java b/libcobj/app/src/main/java/jp/osscons/opensourcecobol/libcobj/data/CobolDataStorage.java index 4ab8779a..691bbaf2 100644 --- a/libcobj/app/src/main/java/jp/osscons/opensourcecobol/libcobj/data/CobolDataStorage.java +++ b/libcobj/app/src/main/java/jp/osscons/opensourcecobol/libcobj/data/CobolDataStorage.java @@ -71,6 +71,11 @@ public CobolDataStorage() { this.data = null; } + public void setDataRefAndIndex(CobolDataStorage data, int index) { + this.data = data.data; + this.index = index; + } + public int getIndex() { return this.index; } diff --git a/libcobj/app/src/main/java/jp/osscons/opensourcecobol/libcobj/file/CobolFileSort.java b/libcobj/app/src/main/java/jp/osscons/opensourcecobol/libcobj/file/CobolFileSort.java index 854f1bf0..116e9322 100644 --- a/libcobj/app/src/main/java/jp/osscons/opensourcecobol/libcobj/file/CobolFileSort.java +++ b/libcobj/app/src/main/java/jp/osscons/opensourcecobol/libcobj/file/CobolFileSort.java @@ -31,6 +31,7 @@ import jp.osscons.opensourcecobol.libcobj.common.CobolUtil; import jp.osscons.opensourcecobol.libcobj.data.AbstractCobolField; import jp.osscons.opensourcecobol.libcobj.data.CobolDataStorage; +import jp.osscons.opensourcecobol.libcobj.data.CobolFieldAttribute; import jp.osscons.opensourcecobol.libcobj.data.CobolFieldFactory; import jp.osscons.opensourcecobol.libcobj.exceptions.CobolStopRunException; @@ -903,4 +904,162 @@ public static void performReturn(CobolFile f) { return; } } + + /* Table sort */ + private static int sortNKeys; + private static CobolFileKey[] sortKeys; + private static CobolDataStorage sortCollate; + private static CobolDataStorage pivotStorage = new CobolDataStorage(); + private static CobolDataStorage pivotLeftStorage = new CobolDataStorage(); + private static CobolDataStorage pivotRightStorage = new CobolDataStorage(); + private static CobolDataStorage leftStorage = new CobolDataStorage(); + private static CobolDataStorage rightStorage = new CobolDataStorage(); + private static CobolDataStorage cmpStorage1 = new CobolDataStorage(); + private static CobolDataStorage cmpStorage2 = new CobolDataStorage(); + private static CobolDataStorage copyBuffer = null; + private static int copyBufferSize = 0; + private static AbstractCobolField cmpField1 = + CobolFieldFactory.makeCobolField( + 0, + cmpStorage1, + new CobolFieldAttribute(CobolFieldAttribute.COB_TYPE_ALPHANUMERIC, 0, 0, 0, null)); + private static AbstractCobolField cmpField2 = + CobolFieldFactory.makeCobolField( + 0, + cmpStorage2, + new CobolFieldAttribute(CobolFieldAttribute.COB_TYPE_ALPHANUMERIC, 0, 0, 0, null)); + + public static void sortTableInit(int nkeys, int collatingSequence) { + sortTableInit(nkeys, null); + } + + public static void sortTableInit(int nkeys, CobolDataStorage collatingSequence) { + sortNKeys = 0; + if (sortKeys == null || sortKeys.length < nkeys) { + sortKeys = new CobolFileKey[nkeys]; + } + if (collatingSequence != null) { + sortCollate = collatingSequence; + } else { + sortCollate = CobolModule.getCurrentModule().collating_sequence; + } + } + + public static void sortTableInitKey(int flag, AbstractCobolField field, int offset) { + if (sortKeys[sortNKeys] == null) { + sortKeys[sortNKeys] = new CobolFileKey(); + } + sortKeys[sortNKeys].setFlag(flag); + sortKeys[sortNKeys].setField( + CobolFieldFactory.makeCobolField( + field.getSize(), (CobolDataStorage) null, field.getAttribute())); + sortKeys[sortNKeys].setOffset(offset); + sortNKeys++; + } + + public static void sortTable(AbstractCobolField f, int n) { + int size = f.getSize(); + if (copyBuffer == null || copyBufferSize < size) { + copyBuffer = new CobolDataStorage(size); + } + fieldQuickSort(f.getDataStorage(), 0, n, f.getSize()); + } + + private static void fieldQuickSort( + CobolDataStorage base, int mostLeft, int mostRight, int recordSize) { + + if (mostRight - mostLeft <= 1) { + return; + } + + int pivot = (mostRight + mostLeft) / 2; + int left = mostLeft, right = mostRight - 1; + + while (true) { + pivotStorage.setDataRefAndIndex(base, base.getIndex() + pivot * recordSize); + boolean elementBiggerThanPivot = false; + for (; left < pivot; ++left) { + leftStorage.setDataRefAndIndex(base, base.getIndex() + left * recordSize); + if (compareStorageForSort(leftStorage, pivotStorage) > 0) { + elementBiggerThanPivot = true; + break; + } + } + boolean elementSmallerThanPivot = false; + for (; right > pivot; --right) { + rightStorage.setDataRefAndIndex(base, base.getIndex() + right * recordSize); + if (compareStorageForSort(pivotStorage, rightStorage) > 0) { + elementSmallerThanPivot = true; + break; + } + } + if (elementBiggerThanPivot && elementSmallerThanPivot) { + swap2Storages(leftStorage, rightStorage, recordSize); + ++left; + --right; + } else if (elementBiggerThanPivot && !elementSmallerThanPivot) { + if (left + 1 == pivot) { + swap2Storages(leftStorage, pivotStorage, recordSize); + } else { + pivotLeftStorage.setDataRefAndIndex(base, base.getIndex() + (pivot - 1) * recordSize); + rotate3Storages(pivotStorage, pivotLeftStorage, leftStorage, recordSize); + } + --pivot; + } else if (!elementBiggerThanPivot && elementSmallerThanPivot) { + if (right - 1 == pivot) { + swap2Storages(pivotStorage, rightStorage, recordSize); + } else { + pivotRightStorage.setDataRefAndIndex(base, base.getIndex() + (pivot + 1) * recordSize); + rotate3Storages(pivotStorage, pivotRightStorage, rightStorage, recordSize); + } + ++pivot; + } else { + break; + } + } + fieldQuickSort(base, mostLeft, pivot, recordSize); + fieldQuickSort(base, pivot + 1, mostRight, recordSize); + } + + private static int compareStorageForSort(CobolDataStorage s1, CobolDataStorage s2) { + for (int i = 0; i < sortNKeys; ++i) { + int keySize = sortKeys[i].getField().getSize(); + cmpField1.setSize(keySize); + cmpField2.setSize(keySize); + + CobolFieldAttribute attr = sortKeys[i].getField().getAttribute(); + cmpField1.setAttribute(attr); + cmpField2.setAttribute(attr); + + cmpStorage1.setDataRefAndIndex(s1, s1.getIndex() + sortKeys[i].getOffset()); + cmpStorage2.setDataRefAndIndex(s2, s2.getIndex() + sortKeys[i].getOffset()); + + int cmp; + if (attr.isTypeNumeric()) { + cmp = cmpField1.numericCompareTo(cmpField2); + } else if (attr.isTypeNational()) { + cmp = CobolUtil.nationalCmps(s1, s2, keySize, sortCollate); + } else { + cmp = cmpField1.cmpAlnum(cmpField2); + } + if (cmp != 0) { + return (sortKeys[i].getFlag() == COB_ASCENDING) ? cmp : -cmp; + } + } + return 0; + } + + private static void swap2Storages(CobolDataStorage s1, CobolDataStorage s2, int size) { + copyBuffer.memcpy(s1, size); + s1.memcpy(s2, size); + s2.memcpy(copyBuffer, size); + } + + private static void rotate3Storages( + CobolDataStorage s1, CobolDataStorage s2, CobolDataStorage s3, int size) { + copyBuffer.memcpy(s3, size); + s3.memcpy(s2, size); + s2.memcpy(s1, size); + s1.memcpy(copyBuffer, size); + } } diff --git a/tests/run.src/miscellaneous.at b/tests/run.src/miscellaneous.at index ae1d7c27..1345a592 100644 --- a/tests/run.src/miscellaneous.at +++ b/tests/run.src/miscellaneous.at @@ -1366,7 +1366,6 @@ AT_CLEANUP AT_SETUP([SORT: table sort]) -AT_CHECK([${SKIP_TEST}]) AT_DATA([prog.cob], [ IDENTIFICATION DIVISION. From b86e4fa95c7bc68301a56fa18c81cb3e89e9880d Mon Sep 17 00:00:00 2001 From: Yutaro Sakamoto Date: Mon, 27 Nov 2023 18:13:53 +0900 Subject: [PATCH 2/3] [Perf]: change the sorting process --- .../libcobj/data/CobolDataStorage.java | 4 + .../libcobj/file/CobolFileSort.java | 75 +++++++++++-------- 2 files changed, 49 insertions(+), 30 deletions(-) diff --git a/libcobj/app/src/main/java/jp/osscons/opensourcecobol/libcobj/data/CobolDataStorage.java b/libcobj/app/src/main/java/jp/osscons/opensourcecobol/libcobj/data/CobolDataStorage.java index 691bbaf2..b8855fb0 100644 --- a/libcobj/app/src/main/java/jp/osscons/opensourcecobol/libcobj/data/CobolDataStorage.java +++ b/libcobj/app/src/main/java/jp/osscons/opensourcecobol/libcobj/data/CobolDataStorage.java @@ -80,6 +80,10 @@ public int getIndex() { return this.index; } + public void setIndex(int index) { + this.index = index; + } + public byte[] getRefOfData() { return this.data; } diff --git a/libcobj/app/src/main/java/jp/osscons/opensourcecobol/libcobj/file/CobolFileSort.java b/libcobj/app/src/main/java/jp/osscons/opensourcecobol/libcobj/file/CobolFileSort.java index 116e9322..5a8629c1 100644 --- a/libcobj/app/src/main/java/jp/osscons/opensourcecobol/libcobj/file/CobolFileSort.java +++ b/libcobj/app/src/main/java/jp/osscons/opensourcecobol/libcobj/file/CobolFileSort.java @@ -910,14 +910,14 @@ public static void performReturn(CobolFile f) { private static CobolFileKey[] sortKeys; private static CobolDataStorage sortCollate; private static CobolDataStorage pivotStorage = new CobolDataStorage(); - private static CobolDataStorage pivotLeftStorage = new CobolDataStorage(); - private static CobolDataStorage pivotRightStorage = new CobolDataStorage(); private static CobolDataStorage leftStorage = new CobolDataStorage(); private static CobolDataStorage rightStorage = new CobolDataStorage(); private static CobolDataStorage cmpStorage1 = new CobolDataStorage(); private static CobolDataStorage cmpStorage2 = new CobolDataStorage(); private static CobolDataStorage copyBuffer = null; - private static int copyBufferSize = 0; + private static CobolDataStorage tmpRecord = new CobolDataStorage(); + private static int copyBufferSizeMax = 0; + private static int[] sortBuffer = null; private static AbstractCobolField cmpField1 = CobolFieldFactory.makeCobolField( 0, @@ -958,14 +958,32 @@ public static void sortTableInitKey(int flag, AbstractCobolField field, int offs } public static void sortTable(AbstractCobolField f, int n) { - int size = f.getSize(); - if (copyBuffer == null || copyBufferSize < size) { - copyBuffer = new CobolDataStorage(size); + int recordSize = f.getSize(); + if (sortBuffer == null || sortBuffer.length < n) { + sortBuffer = new int[n]; } - fieldQuickSort(f.getDataStorage(), 0, n, f.getSize()); + for (int i = 0; i < n; ++i) { + sortBuffer[i] = i; + } + CobolDataStorage baseStorage = f.getDataStorage(); + int baseStorageBaseIndex = baseStorage.getIndex(); + indexQuickSort(f.getDataStorage(), 0, n, recordSize); + + int copyBufferSize = n * f.getSize(); + if (copyBuffer == null || copyBufferSizeMax < copyBufferSize) { + copyBuffer = new CobolDataStorage(copyBufferSize); + copyBufferSizeMax = copyBufferSize; + } + + for (int i = 0; i < n; ++i, copyBuffer.addIndex(recordSize)) { + tmpRecord.setDataRefAndIndex(baseStorage, baseStorageBaseIndex + recordSize * sortBuffer[i]); + copyBuffer.memcpy(tmpRecord, recordSize); + } + copyBuffer.setIndex(0); + baseStorage.memcpy(copyBuffer, copyBufferSize); } - private static void fieldQuickSort( + private static void indexQuickSort( CobolDataStorage base, int mostLeft, int mostRight, int recordSize) { if (mostRight - mostLeft <= 1) { @@ -976,10 +994,10 @@ private static void fieldQuickSort( int left = mostLeft, right = mostRight - 1; while (true) { - pivotStorage.setDataRefAndIndex(base, base.getIndex() + pivot * recordSize); + pivotStorage.setDataRefAndIndex(base, base.getIndex() + sortBuffer[pivot] * recordSize); boolean elementBiggerThanPivot = false; for (; left < pivot; ++left) { - leftStorage.setDataRefAndIndex(base, base.getIndex() + left * recordSize); + leftStorage.setDataRefAndIndex(base, base.getIndex() + sortBuffer[left] * recordSize); if (compareStorageForSort(leftStorage, pivotStorage) > 0) { elementBiggerThanPivot = true; break; @@ -987,38 +1005,36 @@ private static void fieldQuickSort( } boolean elementSmallerThanPivot = false; for (; right > pivot; --right) { - rightStorage.setDataRefAndIndex(base, base.getIndex() + right * recordSize); + rightStorage.setDataRefAndIndex(base, base.getIndex() + sortBuffer[right] * recordSize); if (compareStorageForSort(pivotStorage, rightStorage) > 0) { elementSmallerThanPivot = true; break; } } if (elementBiggerThanPivot && elementSmallerThanPivot) { - swap2Storages(leftStorage, rightStorage, recordSize); + swap2Indecies(left, right); ++left; --right; } else if (elementBiggerThanPivot && !elementSmallerThanPivot) { if (left + 1 == pivot) { - swap2Storages(leftStorage, pivotStorage, recordSize); + swap2Indecies(left, pivot); } else { - pivotLeftStorage.setDataRefAndIndex(base, base.getIndex() + (pivot - 1) * recordSize); - rotate3Storages(pivotStorage, pivotLeftStorage, leftStorage, recordSize); + rotate3Indecies(pivot, pivot - 1, left); } --pivot; } else if (!elementBiggerThanPivot && elementSmallerThanPivot) { if (right - 1 == pivot) { - swap2Storages(pivotStorage, rightStorage, recordSize); + swap2Indecies(pivot, right); } else { - pivotRightStorage.setDataRefAndIndex(base, base.getIndex() + (pivot + 1) * recordSize); - rotate3Storages(pivotStorage, pivotRightStorage, rightStorage, recordSize); + rotate3Indecies(pivot, pivot + 1, right); } ++pivot; } else { break; } } - fieldQuickSort(base, mostLeft, pivot, recordSize); - fieldQuickSort(base, pivot + 1, mostRight, recordSize); + indexQuickSort(base, mostLeft, pivot, recordSize); + indexQuickSort(base, pivot + 1, mostRight, recordSize); } private static int compareStorageForSort(CobolDataStorage s1, CobolDataStorage s2) { @@ -1049,17 +1065,16 @@ private static int compareStorageForSort(CobolDataStorage s1, CobolDataStorage s return 0; } - private static void swap2Storages(CobolDataStorage s1, CobolDataStorage s2, int size) { - copyBuffer.memcpy(s1, size); - s1.memcpy(s2, size); - s2.memcpy(copyBuffer, size); + private static void swap2Indecies(int a, int b) { + int tmp = sortBuffer[a]; + sortBuffer[a] = sortBuffer[b]; + sortBuffer[b] = tmp; } - private static void rotate3Storages( - CobolDataStorage s1, CobolDataStorage s2, CobolDataStorage s3, int size) { - copyBuffer.memcpy(s3, size); - s3.memcpy(s2, size); - s2.memcpy(s1, size); - s1.memcpy(copyBuffer, size); + private static void rotate3Indecies(int a, int b, int c) { + int tmp = sortBuffer[c]; + sortBuffer[c] = sortBuffer[b]; + sortBuffer[b] = sortBuffer[a]; + sortBuffer[a] = tmp; } } From 0c40f36c7e3fb0a584bd46ccc5fd250a6506e7b3 Mon Sep 17 00:00:00 2001 From: Yutaro Sakamoto Date: Mon, 27 Nov 2023 18:33:45 +0900 Subject: [PATCH 3/3] [Update]: improve the test for sorting tables --- tests/run.src/miscellaneous.at | 58 ++++++++++++++++++++++++++-------- 1 file changed, 44 insertions(+), 14 deletions(-) diff --git a/tests/run.src/miscellaneous.at b/tests/run.src/miscellaneous.at index 1345a592..8ca8c3e5 100644 --- a/tests/run.src/miscellaneous.at +++ b/tests/run.src/miscellaneous.at @@ -1373,22 +1373,43 @@ AT_DATA([prog.cob], [ DATA DIVISION. WORKING-STORAGE SECTION. 01 G VALUE "d4b2e1a3c5". - 02 TBL OCCURS 5. + 02 TBL-G OCCURS 5. 03 X PIC X. 03 Y PIC 9. - PROCEDURE DIVISION. - SORT TBL ASCENDING KEY X. - DISPLAY G - END-DISPLAY. - SORT TBL DESCENDING KEY Y. - DISPLAY G - END-DISPLAY. - SORT TBL ASCENDING KEY TBL. - DISPLAY G - END-DISPLAY. - SORT TBL DESCENDING KEY. - DISPLAY G - END-DISPLAY. + 01 H VALUE "d4Db2Be3Ea5Ac6Cf1F". + 02 TBL-H OCCURS 6. + 03 A PIC X. + 03 B PIC 9. + 03 C PIC X. + PROCEDURE DIVISION. + SORT TBL-G ASCENDING KEY X. + DISPLAY G. + SORT TBL-G DESCENDING KEY Y. + DISPLAY G. + SORT TBL-G ASCENDING KEY TBL-G. + DISPLAY G. + SORT TBL-G DESCENDING KEY. + DISPLAY G. + + SORT TBL-H ASCENDING KEY A. + DISPLAY H. + SORT TBL-H DESCENDING KEY B. + DISPLAY H. + SORT TBL-H DESCENDING KEY C. + DISPLAY H. + SORT TBL-H ASCENDING KEY TBL-H. + DISPLAY H. + SORT TBL-H DESCENDING KEY. + DISPLAY H. + + SORT TBL-G ASCENDING KEY X. + DISPLAY G. + SORT TBL-G DESCENDING KEY Y. + DISPLAY G. + SORT TBL-G ASCENDING KEY TBL-G. + DISPLAY G. + SORT TBL-G DESCENDING KEY. + DISPLAY G. STOP RUN. ]) @@ -1398,6 +1419,15 @@ AT_CHECK([java prog], [0], c5d4a3b2e1 a3b2c5d4e1 e1d4c5b2a3 +a5Ab2Bc6Cd4De3Ef1F +c6Ca5Ad4De3Eb2Bf1F +f1Fe3Ed4Dc6Cb2Ba5A +a5Ab2Bc6Cd4De3Ef1F +f1Fe3Ed4Dc6Cb2Ba5A +a3b2c5d4e1 +c5d4a3b2e1 +a3b2c5d4e1 +e1d4c5b2a3 ]) AT_CLEANUP