GitHub user QiangCai opened a pull request:
https://github.com/apache/carbondata/pull/2657 [CARBONDATA-2884] Rename the methods of ByteUtil class to avoid the misuse The method toBytes will execute XOR operation on data. So the result is not the byte array of the real value. Better to rename the methods of ByteUtil class to avoid the misuse - [x] Any interfaces changed? yes, 1. rename the old toBytes methods to toXorBytes 2. new toBytes methods return the real byte array - [x] Any backward compatibility impacted? no - [x] Document update required? yes, added java doc - [x] Testing done Please provide details on - Whether new unit test cases have been added or why no new tests are required? - How it is tested? Please attach test report. added test case - Is it a performance related change? Please attach the performance test report. - Any additional information to help reviewers in testing this change. - [x] For large changes, please consider breaking it into sub-tasks under an umbrella JIRA. small changes You can merge this pull request into a Git repository by running: $ git pull https://github.com/QiangCai/carbondata byteutil Alternatively you can review and apply these changes as the patch at: https://github.com/apache/carbondata/pull/2657.patch To close this pull request, make a commit to your master/trunk branch with (at least) the following in the commit message: This closes #2657 ---- commit e26c3c2d61ea4546754f7e112434dae2d724709f Author: QiangCai <qiangcai@...> Date: 2018-08-24T01:56:43Z rename the methods to avoid the misuse ---- --- |
Github user ravipesala commented on the issue:
https://github.com/apache/carbondata/pull/2657 SDV Build Fail , Please check CI http://144.76.159.231:8080/job/ApacheSDVTests/6367/ --- |
In reply to this post by qiuchenjian-2
Github user CarbonDataQA commented on the issue:
https://github.com/apache/carbondata/pull/2657 Build Failed with Spark 2.1.0, Please check CI http://136.243.101.176:8080/job/ApacheCarbonPRBuilder1/8019/ --- |
In reply to this post by qiuchenjian-2
Github user CarbonDataQA commented on the issue:
https://github.com/apache/carbondata/pull/2657 Build Success with Spark 2.2.1, Please check CI http://88.99.58.216:8080/job/ApacheCarbonPRBuilder/6742/ --- |
In reply to this post by qiuchenjian-2
Github user QiangCai commented on the issue:
https://github.com/apache/carbondata/pull/2657 retest this please --- |
In reply to this post by qiuchenjian-2
Github user CarbonDataQA commented on the issue:
https://github.com/apache/carbondata/pull/2657 Build Success with Spark 2.2.1, Please check CI http://88.99.58.216:8080/job/ApacheCarbonPRBuilder/6746/ --- |
In reply to this post by qiuchenjian-2
Github user CarbonDataQA commented on the issue:
https://github.com/apache/carbondata/pull/2657 Build Success with Spark 2.1.0, Please check CI http://136.243.101.176:8080/job/ApacheCarbonPRBuilder1/8023/ --- |
In reply to this post by qiuchenjian-2
Github user QiangCai commented on the issue:
https://github.com/apache/carbondata/pull/2657 retest dev please --- |
In reply to this post by qiuchenjian-2
Github user jackylk commented on a diff in the pull request:
https://github.com/apache/carbondata/pull/2657#discussion_r212822416 --- Diff: core/src/test/java/org/apache/carbondata/core/util/ByteUtilTest.java --- @@ -17,156 +17,246 @@ package org.apache.carbondata.core.util; import junit.framework.TestCase; + import org.apache.carbondata.core.constants.CarbonCommonConstants; import org.apache.carbondata.core.util.ByteUtil.UnsafeComparer; + import org.junit.Before; import org.junit.Test; import java.nio.ByteBuffer; import java.nio.charset.Charset; - /** * This test will test the functionality of the Byte Util * for the comparision of 2 byte buffers */ public class ByteUtilTest extends TestCase { - String dimensionValue1 = "aaaaaaaa1235"; - String dimensionValue2 = "aaaaaaaa1234"; - private ByteBuffer buff1; - private ByteBuffer buff2; - - /** - * This method will form one single byte [] for all the high card dims. - * - * @param byteBufferArr - * @return - */ - public static byte[] packByteBufferIntoSingleByteArray( - ByteBuffer[] byteBufferArr) { - // for empty array means there is no data to remove dictionary. - if (null == byteBufferArr || byteBufferArr.length == 0) { - return null; - } - int noOfCol = byteBufferArr.length; - short toDetermineLengthOfByteArr = 2; - short offsetLen = (short) (noOfCol * 2 + toDetermineLengthOfByteArr); - int totalBytes = calculateTotalBytes(byteBufferArr) + offsetLen; - - ByteBuffer buffer = ByteBuffer.allocate(totalBytes); - - // write the length of the byte [] as first short - buffer.putShort((short) (totalBytes - toDetermineLengthOfByteArr)); - // writing the offset of the first element. - buffer.putShort(offsetLen); - - // prepare index for byte [] - for (int index = 0; index < byteBufferArr.length - 1; index++) { - ByteBuffer individualCol = byteBufferArr[index]; - // short lengthOfbytes = individualCol.getShort(); - int noOfBytes = individualCol.capacity(); - - buffer.putShort((short) (offsetLen + noOfBytes)); - offsetLen += noOfBytes; - individualCol.rewind(); - } - - // put actual data. - for (int index = 0; index < byteBufferArr.length; index++) { - ByteBuffer individualCol = byteBufferArr[index]; - buffer.put(individualCol.array()); - } - - buffer.rewind(); - return buffer.array(); + String dimensionValue1 = "aaaaaaaa1235"; + String dimensionValue2 = "aaaaaaaa1234"; + private ByteBuffer buff1; + private ByteBuffer buff2; + /** + * This method will form one single byte [] for all the high card dims. + * + * @param byteBufferArr + * @return --- End diff -- please complete the comment --- |
In reply to this post by qiuchenjian-2
Github user jackylk commented on a diff in the pull request:
https://github.com/apache/carbondata/pull/2657#discussion_r212822441 --- Diff: core/src/test/java/org/apache/carbondata/core/util/ByteUtilTest.java --- @@ -17,156 +17,246 @@ package org.apache.carbondata.core.util; import junit.framework.TestCase; + import org.apache.carbondata.core.constants.CarbonCommonConstants; import org.apache.carbondata.core.util.ByteUtil.UnsafeComparer; + import org.junit.Before; import org.junit.Test; import java.nio.ByteBuffer; import java.nio.charset.Charset; - /** * This test will test the functionality of the Byte Util * for the comparision of 2 byte buffers */ public class ByteUtilTest extends TestCase { - String dimensionValue1 = "aaaaaaaa1235"; - String dimensionValue2 = "aaaaaaaa1234"; - private ByteBuffer buff1; - private ByteBuffer buff2; - - /** - * This method will form one single byte [] for all the high card dims. - * - * @param byteBufferArr - * @return - */ - public static byte[] packByteBufferIntoSingleByteArray( - ByteBuffer[] byteBufferArr) { - // for empty array means there is no data to remove dictionary. - if (null == byteBufferArr || byteBufferArr.length == 0) { - return null; - } - int noOfCol = byteBufferArr.length; - short toDetermineLengthOfByteArr = 2; - short offsetLen = (short) (noOfCol * 2 + toDetermineLengthOfByteArr); - int totalBytes = calculateTotalBytes(byteBufferArr) + offsetLen; - - ByteBuffer buffer = ByteBuffer.allocate(totalBytes); - - // write the length of the byte [] as first short - buffer.putShort((short) (totalBytes - toDetermineLengthOfByteArr)); - // writing the offset of the first element. - buffer.putShort(offsetLen); - - // prepare index for byte [] - for (int index = 0; index < byteBufferArr.length - 1; index++) { - ByteBuffer individualCol = byteBufferArr[index]; - // short lengthOfbytes = individualCol.getShort(); - int noOfBytes = individualCol.capacity(); - - buffer.putShort((short) (offsetLen + noOfBytes)); - offsetLen += noOfBytes; - individualCol.rewind(); - } - - // put actual data. - for (int index = 0; index < byteBufferArr.length; index++) { - ByteBuffer individualCol = byteBufferArr[index]; - buffer.put(individualCol.array()); - } - - buffer.rewind(); - return buffer.array(); + String dimensionValue1 = "aaaaaaaa1235"; + String dimensionValue2 = "aaaaaaaa1234"; + private ByteBuffer buff1; + private ByteBuffer buff2; + /** + * This method will form one single byte [] for all the high card dims. + * + * @param byteBufferArr + * @return + */ + public static byte[] packByteBufferIntoSingleByteArray(ByteBuffer[] byteBufferArr) { + // for empty array means there is no data to remove dictionary. + if (null == byteBufferArr || byteBufferArr.length == 0) { + return null; } + int noOfCol = byteBufferArr.length; + short toDetermineLengthOfByteArr = 2; + short offsetLen = (short) (noOfCol * 2 + toDetermineLengthOfByteArr); + int totalBytes = calculateTotalBytes(byteBufferArr) + offsetLen; - /** - * To calculate the total bytes in byte Buffer[]. - * - * @param byteBufferArr - * @return - */ - private static int calculateTotalBytes(ByteBuffer[] byteBufferArr) { - int total = 0; - for (int index = 0; index < byteBufferArr.length; index++) { - total += byteBufferArr[index].capacity(); - } - return total; - } + ByteBuffer buffer = ByteBuffer.allocate(totalBytes); - /** - * @throws Exception - */ - @Before - public void setUp() throws Exception { + // write the length of the byte [] as first short + buffer.putShort((short) (totalBytes - toDetermineLengthOfByteArr)); + // writing the offset of the first element. + buffer.putShort(offsetLen); - } - - @Test - public void testLessThan() { - dimensionValue1 = "aaaaa6aa1235"; - dimensionValue2 = "aaaaa5aa1234"; + // prepare index for byte [] + for (int index = 0; index < byteBufferArr.length - 1; index++) { + ByteBuffer individualCol = byteBufferArr[index]; + // short lengthOfbytes = individualCol.getShort(); + int noOfBytes = individualCol.capacity(); - prepareBuffers(); - assertFalse(UnsafeComparer.INSTANCE.compareTo(buff1, buff2) < 0); + buffer.putShort((short) (offsetLen + noOfBytes)); + offsetLen += noOfBytes; + individualCol.rewind(); } - @Test - public void testIntConversion() { - byte[] data = new byte[4]; - ByteUtil.setInt(data, 0, 968); - assertEquals(ByteUtil.toInt(data, 0), 968); + + // put actual data. + for (int index = 0; index < byteBufferArr.length; index++) { + ByteBuffer individualCol = byteBufferArr[index]; + buffer.put(individualCol.array()); } - @Test - public void testEqualToCase() { - dimensionValue1 = "aaaaaaaa1234"; - dimensionValue2 = "aaaaaaaa1234"; + buffer.rewind(); + return buffer.array(); - prepareBuffers(); - assertTrue(UnsafeComparer.INSTANCE.compareTo(buff1, buff2) == 0); + } + + /** + * To calculate the total bytes in byte Buffer[]. + * + * @param byteBufferArr + * @return + */ + private static int calculateTotalBytes(ByteBuffer[] byteBufferArr) { + int total = 0; + for (int index = 0; index < byteBufferArr.length; index++) { + total += byteBufferArr[index].capacity(); } + return total; + } - @Test - public void testLessThanInBoundaryCondition() { - dimensionValue1 = "aaaaaaaa12341"; - dimensionValue2 = "aaaaaaaa12344"; + /** + * @throws Exception + */ + @Before public void setUp() throws Exception { - prepareBuffers(); - assertTrue(UnsafeComparer.INSTANCE.compareTo(buff1, buff2) < 0); - } + } - /** - * This will prepare the byte buffers in the required format for comparision. - */ - private void prepareBuffers() { - ByteBuffer[] out1 = new ByteBuffer[1]; - ByteBuffer buffer = ByteBuffer.allocate(dimensionValue1.length()); - buffer.put(dimensionValue1.getBytes(Charset.forName(CarbonCommonConstants.DEFAULT_CHARSET))); - buffer.rewind(); - out1[0] = buffer; + @Test public void testLessThan() { + dimensionValue1 = "aaaaa6aa1235"; + dimensionValue2 = "aaaaa5aa1234"; + prepareBuffers(); + assertFalse(UnsafeComparer.INSTANCE.compareTo(buff1, buff2) < 0); + } - ByteBuffer[] out2 = new ByteBuffer[1]; + @Test public void testIntConversion() { + byte[] data = new byte[4]; + ByteUtil.setInt(data, 0, 968); + assertEquals(ByteUtil.toInt(data, 0), 968); + } - ByteBuffer buffer2 = ByteBuffer.allocate(dimensionValue2.length()); - buffer2.put(dimensionValue2.getBytes(Charset.forName(CarbonCommonConstants.DEFAULT_CHARSET))); - buffer2.rewind(); - out2[0] = buffer2; + @Test public void testEqualToCase() { + dimensionValue1 = "aaaaaaaa1234"; + dimensionValue2 = "aaaaaaaa1234"; - byte[] arr1 = packByteBufferIntoSingleByteArray(out1); - byte[] arr2 = packByteBufferIntoSingleByteArray(out2); + prepareBuffers(); + assertTrue(UnsafeComparer.INSTANCE.compareTo(buff1, buff2) == 0); + } - buff1 = ByteBuffer.wrap(arr1); + @Test public void testLessThanInBoundaryCondition() { --- End diff -- move annotation to previous line --- |
In reply to this post by qiuchenjian-2
Github user ravipesala commented on the issue:
https://github.com/apache/carbondata/pull/2657 SDV Build Fail , Please check CI http://144.76.159.231:8080/job/ApacheSDVTests/6470/ --- |
In reply to this post by qiuchenjian-2
Github user QiangCai commented on a diff in the pull request:
https://github.com/apache/carbondata/pull/2657#discussion_r213886769 --- Diff: core/src/test/java/org/apache/carbondata/core/util/ByteUtilTest.java --- @@ -17,156 +17,246 @@ package org.apache.carbondata.core.util; import junit.framework.TestCase; + import org.apache.carbondata.core.constants.CarbonCommonConstants; import org.apache.carbondata.core.util.ByteUtil.UnsafeComparer; + import org.junit.Before; import org.junit.Test; import java.nio.ByteBuffer; import java.nio.charset.Charset; - /** * This test will test the functionality of the Byte Util * for the comparision of 2 byte buffers */ public class ByteUtilTest extends TestCase { - String dimensionValue1 = "aaaaaaaa1235"; - String dimensionValue2 = "aaaaaaaa1234"; - private ByteBuffer buff1; - private ByteBuffer buff2; - - /** - * This method will form one single byte [] for all the high card dims. - * - * @param byteBufferArr - * @return - */ - public static byte[] packByteBufferIntoSingleByteArray( - ByteBuffer[] byteBufferArr) { - // for empty array means there is no data to remove dictionary. - if (null == byteBufferArr || byteBufferArr.length == 0) { - return null; - } - int noOfCol = byteBufferArr.length; - short toDetermineLengthOfByteArr = 2; - short offsetLen = (short) (noOfCol * 2 + toDetermineLengthOfByteArr); - int totalBytes = calculateTotalBytes(byteBufferArr) + offsetLen; - - ByteBuffer buffer = ByteBuffer.allocate(totalBytes); - - // write the length of the byte [] as first short - buffer.putShort((short) (totalBytes - toDetermineLengthOfByteArr)); - // writing the offset of the first element. - buffer.putShort(offsetLen); - - // prepare index for byte [] - for (int index = 0; index < byteBufferArr.length - 1; index++) { - ByteBuffer individualCol = byteBufferArr[index]; - // short lengthOfbytes = individualCol.getShort(); - int noOfBytes = individualCol.capacity(); - - buffer.putShort((short) (offsetLen + noOfBytes)); - offsetLen += noOfBytes; - individualCol.rewind(); - } - - // put actual data. - for (int index = 0; index < byteBufferArr.length; index++) { - ByteBuffer individualCol = byteBufferArr[index]; - buffer.put(individualCol.array()); - } - - buffer.rewind(); - return buffer.array(); + String dimensionValue1 = "aaaaaaaa1235"; + String dimensionValue2 = "aaaaaaaa1234"; + private ByteBuffer buff1; + private ByteBuffer buff2; + /** + * This method will form one single byte [] for all the high card dims. + * + * @param byteBufferArr + * @return --- End diff -- fixed --- |
In reply to this post by qiuchenjian-2
Github user QiangCai commented on a diff in the pull request:
https://github.com/apache/carbondata/pull/2657#discussion_r213886786 --- Diff: core/src/test/java/org/apache/carbondata/core/util/ByteUtilTest.java --- @@ -17,156 +17,246 @@ package org.apache.carbondata.core.util; import junit.framework.TestCase; + import org.apache.carbondata.core.constants.CarbonCommonConstants; import org.apache.carbondata.core.util.ByteUtil.UnsafeComparer; + import org.junit.Before; import org.junit.Test; import java.nio.ByteBuffer; import java.nio.charset.Charset; - /** * This test will test the functionality of the Byte Util * for the comparision of 2 byte buffers */ public class ByteUtilTest extends TestCase { - String dimensionValue1 = "aaaaaaaa1235"; - String dimensionValue2 = "aaaaaaaa1234"; - private ByteBuffer buff1; - private ByteBuffer buff2; - - /** - * This method will form one single byte [] for all the high card dims. - * - * @param byteBufferArr - * @return - */ - public static byte[] packByteBufferIntoSingleByteArray( - ByteBuffer[] byteBufferArr) { - // for empty array means there is no data to remove dictionary. - if (null == byteBufferArr || byteBufferArr.length == 0) { - return null; - } - int noOfCol = byteBufferArr.length; - short toDetermineLengthOfByteArr = 2; - short offsetLen = (short) (noOfCol * 2 + toDetermineLengthOfByteArr); - int totalBytes = calculateTotalBytes(byteBufferArr) + offsetLen; - - ByteBuffer buffer = ByteBuffer.allocate(totalBytes); - - // write the length of the byte [] as first short - buffer.putShort((short) (totalBytes - toDetermineLengthOfByteArr)); - // writing the offset of the first element. - buffer.putShort(offsetLen); - - // prepare index for byte [] - for (int index = 0; index < byteBufferArr.length - 1; index++) { - ByteBuffer individualCol = byteBufferArr[index]; - // short lengthOfbytes = individualCol.getShort(); - int noOfBytes = individualCol.capacity(); - - buffer.putShort((short) (offsetLen + noOfBytes)); - offsetLen += noOfBytes; - individualCol.rewind(); - } - - // put actual data. - for (int index = 0; index < byteBufferArr.length; index++) { - ByteBuffer individualCol = byteBufferArr[index]; - buffer.put(individualCol.array()); - } - - buffer.rewind(); - return buffer.array(); + String dimensionValue1 = "aaaaaaaa1235"; + String dimensionValue2 = "aaaaaaaa1234"; + private ByteBuffer buff1; + private ByteBuffer buff2; + /** + * This method will form one single byte [] for all the high card dims. + * + * @param byteBufferArr + * @return + */ + public static byte[] packByteBufferIntoSingleByteArray(ByteBuffer[] byteBufferArr) { + // for empty array means there is no data to remove dictionary. + if (null == byteBufferArr || byteBufferArr.length == 0) { + return null; } + int noOfCol = byteBufferArr.length; + short toDetermineLengthOfByteArr = 2; + short offsetLen = (short) (noOfCol * 2 + toDetermineLengthOfByteArr); + int totalBytes = calculateTotalBytes(byteBufferArr) + offsetLen; - /** - * To calculate the total bytes in byte Buffer[]. - * - * @param byteBufferArr - * @return - */ - private static int calculateTotalBytes(ByteBuffer[] byteBufferArr) { - int total = 0; - for (int index = 0; index < byteBufferArr.length; index++) { - total += byteBufferArr[index].capacity(); - } - return total; - } + ByteBuffer buffer = ByteBuffer.allocate(totalBytes); - /** - * @throws Exception - */ - @Before - public void setUp() throws Exception { + // write the length of the byte [] as first short + buffer.putShort((short) (totalBytes - toDetermineLengthOfByteArr)); + // writing the offset of the first element. + buffer.putShort(offsetLen); - } - - @Test - public void testLessThan() { - dimensionValue1 = "aaaaa6aa1235"; - dimensionValue2 = "aaaaa5aa1234"; + // prepare index for byte [] + for (int index = 0; index < byteBufferArr.length - 1; index++) { + ByteBuffer individualCol = byteBufferArr[index]; + // short lengthOfbytes = individualCol.getShort(); + int noOfBytes = individualCol.capacity(); - prepareBuffers(); - assertFalse(UnsafeComparer.INSTANCE.compareTo(buff1, buff2) < 0); + buffer.putShort((short) (offsetLen + noOfBytes)); + offsetLen += noOfBytes; + individualCol.rewind(); } - @Test - public void testIntConversion() { - byte[] data = new byte[4]; - ByteUtil.setInt(data, 0, 968); - assertEquals(ByteUtil.toInt(data, 0), 968); + + // put actual data. + for (int index = 0; index < byteBufferArr.length; index++) { + ByteBuffer individualCol = byteBufferArr[index]; + buffer.put(individualCol.array()); } - @Test - public void testEqualToCase() { - dimensionValue1 = "aaaaaaaa1234"; - dimensionValue2 = "aaaaaaaa1234"; + buffer.rewind(); + return buffer.array(); - prepareBuffers(); - assertTrue(UnsafeComparer.INSTANCE.compareTo(buff1, buff2) == 0); + } + + /** + * To calculate the total bytes in byte Buffer[]. + * + * @param byteBufferArr + * @return + */ + private static int calculateTotalBytes(ByteBuffer[] byteBufferArr) { + int total = 0; + for (int index = 0; index < byteBufferArr.length; index++) { + total += byteBufferArr[index].capacity(); } + return total; + } - @Test - public void testLessThanInBoundaryCondition() { - dimensionValue1 = "aaaaaaaa12341"; - dimensionValue2 = "aaaaaaaa12344"; + /** + * @throws Exception + */ + @Before public void setUp() throws Exception { - prepareBuffers(); - assertTrue(UnsafeComparer.INSTANCE.compareTo(buff1, buff2) < 0); - } + } - /** - * This will prepare the byte buffers in the required format for comparision. - */ - private void prepareBuffers() { - ByteBuffer[] out1 = new ByteBuffer[1]; - ByteBuffer buffer = ByteBuffer.allocate(dimensionValue1.length()); - buffer.put(dimensionValue1.getBytes(Charset.forName(CarbonCommonConstants.DEFAULT_CHARSET))); - buffer.rewind(); - out1[0] = buffer; + @Test public void testLessThan() { + dimensionValue1 = "aaaaa6aa1235"; + dimensionValue2 = "aaaaa5aa1234"; + prepareBuffers(); + assertFalse(UnsafeComparer.INSTANCE.compareTo(buff1, buff2) < 0); + } - ByteBuffer[] out2 = new ByteBuffer[1]; + @Test public void testIntConversion() { + byte[] data = new byte[4]; + ByteUtil.setInt(data, 0, 968); + assertEquals(ByteUtil.toInt(data, 0), 968); + } - ByteBuffer buffer2 = ByteBuffer.allocate(dimensionValue2.length()); - buffer2.put(dimensionValue2.getBytes(Charset.forName(CarbonCommonConstants.DEFAULT_CHARSET))); - buffer2.rewind(); - out2[0] = buffer2; + @Test public void testEqualToCase() { + dimensionValue1 = "aaaaaaaa1234"; + dimensionValue2 = "aaaaaaaa1234"; - byte[] arr1 = packByteBufferIntoSingleByteArray(out1); - byte[] arr2 = packByteBufferIntoSingleByteArray(out2); + prepareBuffers(); + assertTrue(UnsafeComparer.INSTANCE.compareTo(buff1, buff2) == 0); + } - buff1 = ByteBuffer.wrap(arr1); + @Test public void testLessThanInBoundaryCondition() { --- End diff -- fixed --- |
In reply to this post by qiuchenjian-2
Github user ravipesala commented on the issue:
https://github.com/apache/carbondata/pull/2657 SDV Build Fail , Please check CI http://144.76.159.231:8080/job/ApacheSDVTests/6472/ --- |
In reply to this post by qiuchenjian-2
Github user CarbonDataQA commented on the issue:
https://github.com/apache/carbondata/pull/2657 Build Success with Spark 2.1.0, Please check CI http://136.243.101.176:8080/job/ApacheCarbonPRBuilder1/8161/ --- |
In reply to this post by qiuchenjian-2
Github user CarbonDataQA commented on the issue:
https://github.com/apache/carbondata/pull/2657 Build Success with Spark 2.2.1, Please check CI http://95.216.28.178:8080/job/ApacheCarbonPRBuilder1/90/ --- |
In reply to this post by qiuchenjian-2
Github user QiangCai commented on the issue:
https://github.com/apache/carbondata/pull/2657 retest sdv please --- |
In reply to this post by qiuchenjian-2
|
In reply to this post by qiuchenjian-2
|
Free forum by Nabble | Edit this page |