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..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 @@ -71,10 +71,19 @@ 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; } + 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 854f1bf0..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 @@ -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,177 @@ 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 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 CobolDataStorage tmpRecord = new CobolDataStorage(); + private static int copyBufferSizeMax = 0; + private static int[] sortBuffer = null; + 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 recordSize = f.getSize(); + if (sortBuffer == null || sortBuffer.length < n) { + sortBuffer = new int[n]; + } + 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 indexQuickSort( + 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() + sortBuffer[pivot] * recordSize); + boolean elementBiggerThanPivot = false; + for (; left < pivot; ++left) { + leftStorage.setDataRefAndIndex(base, base.getIndex() + sortBuffer[left] * recordSize); + if (compareStorageForSort(leftStorage, pivotStorage) > 0) { + elementBiggerThanPivot = true; + break; + } + } + boolean elementSmallerThanPivot = false; + for (; right > pivot; --right) { + rightStorage.setDataRefAndIndex(base, base.getIndex() + sortBuffer[right] * recordSize); + if (compareStorageForSort(pivotStorage, rightStorage) > 0) { + elementSmallerThanPivot = true; + break; + } + } + if (elementBiggerThanPivot && elementSmallerThanPivot) { + swap2Indecies(left, right); + ++left; + --right; + } else if (elementBiggerThanPivot && !elementSmallerThanPivot) { + if (left + 1 == pivot) { + swap2Indecies(left, pivot); + } else { + rotate3Indecies(pivot, pivot - 1, left); + } + --pivot; + } else if (!elementBiggerThanPivot && elementSmallerThanPivot) { + if (right - 1 == pivot) { + swap2Indecies(pivot, right); + } else { + rotate3Indecies(pivot, pivot + 1, right); + } + ++pivot; + } else { + break; + } + } + indexQuickSort(base, mostLeft, pivot, recordSize); + indexQuickSort(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 swap2Indecies(int a, int b) { + int tmp = sortBuffer[a]; + sortBuffer[a] = sortBuffer[b]; + sortBuffer[b] = tmp; + } + + 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; + } } diff --git a/tests/run.src/miscellaneous.at b/tests/run.src/miscellaneous.at index ae1d7c27..8ca8c3e5 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. @@ -1374,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. ]) @@ -1399,6 +1419,15 @@ AT_CHECK([java prog], [0], c5d4a3b2e1 a3b2c5d4e1 e1d4c5b2a3 +a5Ab2Bc6Cd4De3Ef1F +c6Ca5Ad4De3Eb2Bf1F +f1Fe3Ed4Dc6Cb2Ba5A +a5Ab2Bc6Cd4De3Ef1F +f1Fe3Ed4Dc6Cb2Ba5A +a3b2c5d4e1 +c5d4a3b2e1 +a3b2c5d4e1 +e1d4c5b2a3 ]) AT_CLEANUP