From a749a59e2a7c971f4fd160934e942cd7312b0a30 Mon Sep 17 00:00:00 2001 From: levBagryansky <28lev11@gmail.com> Date: Mon, 8 Jul 2024 17:17:06 +0300 Subject: [PATCH 1/3] #3239: BytesOf refactoring --- .../src/main/java/org/eolang/BytesOf.java | 173 ++---------- .../src/main/java/org/eolang/BytesRaw.java | 252 ++++++++++++++++++ 2 files changed, 268 insertions(+), 157 deletions(-) create mode 100644 eo-runtime/src/main/java/org/eolang/BytesRaw.java diff --git a/eo-runtime/src/main/java/org/eolang/BytesOf.java b/eo-runtime/src/main/java/org/eolang/BytesOf.java index af6260bd6d..d6187c6ec9 100644 --- a/eo-runtime/src/main/java/org/eolang/BytesOf.java +++ b/eo-runtime/src/main/java/org/eolang/BytesOf.java @@ -31,27 +31,22 @@ * Bytes. * * @since 1.0 - * @todo #3160:90min This class requires refactoring. As a result of refactoring you should remove - * {@code @SuppressWarnings("PMD.GodClass")} from this class and - * {@code @SuppressWarnings("PMD.CognitiveComplexity")} from {@link BytesOf#shift} method. You can - * check description of this rules here - * pmd.github.io */ @Versionized -@SuppressWarnings({"PMD.TooManyMethods", "PMD.GodClass"}) +@SuppressWarnings("PMD.TooManyMethods") public final class BytesOf implements Bytes { /** - * Binary data. + * Bytes. */ - private final byte[] data; + private final Bytes bytes; /** * Ctor. * @param data Data. */ public BytesOf(final byte[] data) { - this.data = Arrays.copyOf(data, data.length); + this.bytes = new BytesRaw(Arrays.copyOf(data, data.length)); } /** @@ -96,200 +91,64 @@ public BytesOf(final double number) { @Override public Bytes not() { - final byte[] copy = this.take(); - for (int index = 0; index < copy.length; index += 1) { - copy[index] = (byte) ~copy[index]; - } - return new BytesOf(copy); + return this.bytes.not(); } @Override public Bytes and(final Bytes other) { - final byte[] first = this.take(); - final byte[] second = other.take(); - for (int index = 0; index < Math.min(first.length, second.length); index += 1) { - first[index] = (byte) (first[index] & second[index]); - } - return new BytesOf(first); + return this.bytes.and(other); } @Override @SuppressWarnings("PMD.ShortMethodName") public Bytes or(final Bytes other) { - final byte[] first = this.take(); - final byte[] second = other.take(); - for (int index = 0; index < Math.min(first.length, second.length); index += 1) { - first[index] = (byte) (first[index] | second[index]); - } - return new BytesOf(first); + return this.bytes.or(other); } @Override public Bytes xor(final Bytes other) { - final byte[] first = this.take(); - final byte[] second = other.take(); - for (int index = 0; index < Math.min(first.length, second.length); index += 1) { - first[index] = (byte) (first[index] ^ second[index]); - } - return new BytesOf(first); + return this.bytes.xor(other); } @Override @SuppressWarnings("PMD.CognitiveComplexity") public Bytes shift(final int bits) { - // @checkstyle MethodBodyCommentsCheck (3 lines) - // @checkstyle NestedIfDepthCheck (40 lines) - final byte[] bytes = this.take(); - final int mod = Math.abs(bits) % Byte.SIZE; - final int offset = Math.abs(bits) / Byte.SIZE; - if (bits < 0) { - final byte carry = (byte) ((0x01 << mod) - 1); - for (int index = 0; index < bytes.length; index += 1) { - final int source = index + offset; - if (source >= bytes.length) { - bytes[index] = 0; - } else { - byte dst = (byte) (bytes[source] << mod); - if (source + 1 < bytes.length) { - dst |= bytes[source + 1] >>> (Byte.SIZE - mod) & (carry & 0xFF); - } - bytes[index] = dst; - } - } - } else { - final byte carry = (byte) (0xFF << (Byte.SIZE - mod)); - for (int index = bytes.length - 1; index >= 0; index -= 1) { - final int source = index - offset; - if (source < 0) { - bytes[index] = 0; - } else { - byte dst = (byte) ((0xff & bytes[source]) >>> mod); - if (source - 1 >= 0) { - dst |= bytes[source - 1] << (Byte.SIZE - mod) & (carry & 0xFF); - } - bytes[index] = dst; - } - } - } - return new BytesOf(bytes); + return this.bytes.shift(bits); } @Override public Bytes sshift(final int bits) { - if (bits < 0) { - throw new UnsupportedOperationException("right sshift is NYI"); - } - final byte[] bytes = this.shift(bits).take(); - if (this.take()[0] < 0) { - for (int index = 0; index < bytes.length; index += 1) { - final int zeros = BytesOf.numberOfLeadingZeros( - bytes[index] - ); - bytes[index] = (byte) (bytes[index] & 0xFF ^ (-1 << (Byte.SIZE - zeros))); - if (zeros != Byte.SIZE) { - break; - } - } - } - return new BytesOf(bytes); + return this.bytes.sshift(bits); } @Override public T asNumber(final Class type) { - final byte[] ret = this.take(); - final Object res; - final ByteBuffer buf = ByteBuffer.wrap(ret); - if (Long.class.equals(type) - && ret.length == Long.BYTES) { - res = buf.getLong(); - } else if (Integer.class.equals(type) - && ret.length == Integer.BYTES - ) { - res = buf.getInt(); - } else if (Double.class.equals(type) - && ret.length == Double.BYTES - ) { - res = buf.getDouble(); - } else { - throw new UnsupportedOperationException( - String.format( - "Unsupported conversion to '%s' for %d bytes", - type, - ret.length - ) - ); - } - return type.cast(res); + return this.bytes.asNumber(type); } @Override public String asString() { - final StringBuilder out = new StringBuilder(0); - for (final byte bte : this.data) { - if (out.length() > 0) { - out.append('-'); - } - out.append(String.format("%02X", bte)); - } - if (this.data.length == 0) { - out.append("--"); - } - return out.toString(); + return this.bytes.asString(); } @Override public byte[] take() { - return Arrays.copyOf(this.data, this.data.length); + return this.bytes.take(); } @Override public String toString() { - return String.format("BytesOf{%s}", Arrays.toString(this.data)); + return this.bytes.toString(); } @Override public boolean equals(final Object other) { - final boolean result; - if (this == other) { - result = true; - } else if (other instanceof Bytes) { - result = Arrays.equals(this.data, ((Bytes) other).take()); - } else { - result = false; - } - return result; + return this.bytes.equals(other); } @Override public int hashCode() { - return Arrays.hashCode(this.data); - } - - /** - * Count leading zero bits. - * @param num Byte. - * @return Number between 0 and 8. - */ - private static byte numberOfLeadingZeros(final byte num) { - final byte result; - if (num == 0) { - result = (byte) Byte.SIZE; - } else if (num < 0) { - result = (byte) 0; - } else { - byte temp = num; - int bts = Byte.SIZE - 1; - if (temp >= 1 << 4) { - bts -= 4; - temp >>>= 4; - } - if (temp >= 1 << 2) { - bts -= 2; - temp >>>= 2; - } - result = (byte) (bts - (temp >>> 1)); - } - return result; + return this.bytes.hashCode(); } } diff --git a/eo-runtime/src/main/java/org/eolang/BytesRaw.java b/eo-runtime/src/main/java/org/eolang/BytesRaw.java new file mode 100644 index 0000000000..93331d3cee --- /dev/null +++ b/eo-runtime/src/main/java/org/eolang/BytesRaw.java @@ -0,0 +1,252 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016-2024 Objectionary.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.eolang; + +import java.nio.ByteBuffer; +import java.util.Arrays; + +/** + * Bytes to be created from byte array only. + * + * @since 1.0 + * @todo #3239:90min Method {@link BytesOf#shift} should be refactored to get rid of + * {@code @SuppressWarnings("PMD.CognitiveComplexity")} warning. You can + * check description of this rules here + * pmd.github.io + */ +@Versionized +@SuppressWarnings("PMD.TooManyMethods") +class BytesRaw implements Bytes { + + /** + * Binary data. + */ + private final byte[] data; + + /** + * Ctor. + * @param data Data. + */ + BytesRaw(final byte[] data) { + this.data = Arrays.copyOf(data, data.length); + } + + @Override + public Bytes not() { + final byte[] copy = this.take(); + for (int index = 0; index < copy.length; index += 1) { + copy[index] = (byte) ~copy[index]; + } + return new BytesOf(copy); + } + + @Override + public Bytes and(final Bytes other) { + final byte[] first = this.take(); + final byte[] second = other.take(); + for (int index = 0; index < Math.min(first.length, second.length); index += 1) { + first[index] = (byte) (first[index] & second[index]); + } + return new BytesOf(first); + } + + @Override + @SuppressWarnings("PMD.ShortMethodName") + public Bytes or(final Bytes other) { + final byte[] first = this.take(); + final byte[] second = other.take(); + for (int index = 0; index < Math.min(first.length, second.length); index += 1) { + first[index] = (byte) (first[index] | second[index]); + } + return new BytesOf(first); + } + + @Override + public Bytes xor(final Bytes other) { + final byte[] first = this.take(); + final byte[] second = other.take(); + for (int index = 0; index < Math.min(first.length, second.length); index += 1) { + first[index] = (byte) (first[index] ^ second[index]); + } + return new BytesOf(first); + } + + @Override + @SuppressWarnings("PMD.CognitiveComplexity") + public Bytes shift(final int bits) { + // @checkstyle MethodBodyCommentsCheck (3 lines) + // @checkstyle NestedIfDepthCheck (40 lines) + final byte[] bytes = this.take(); + final int mod = Math.abs(bits) % Byte.SIZE; + final int offset = Math.abs(bits) / Byte.SIZE; + if (bits < 0) { + final byte carry = (byte) ((0x01 << mod) - 1); + for (int index = 0; index < bytes.length; index += 1) { + final int source = index + offset; + if (source >= bytes.length) { + bytes[index] = 0; + } else { + byte dst = (byte) (bytes[source] << mod); + if (source + 1 < bytes.length) { + dst |= bytes[source + 1] >>> (Byte.SIZE - mod) & (carry & 0xFF); + } + bytes[index] = dst; + } + } + } else { + final byte carry = (byte) (0xFF << (Byte.SIZE - mod)); + for (int index = bytes.length - 1; index >= 0; index -= 1) { + final int source = index - offset; + if (source < 0) { + bytes[index] = 0; + } else { + byte dst = (byte) ((0xff & bytes[source]) >>> mod); + if (source - 1 >= 0) { + dst |= bytes[source - 1] << (Byte.SIZE - mod) & (carry & 0xFF); + } + bytes[index] = dst; + } + } + } + return new BytesOf(bytes); + } + + @Override + public Bytes sshift(final int bits) { + if (bits < 0) { + throw new UnsupportedOperationException("right sshift is NYI"); + } + final byte[] bytes = this.shift(bits).take(); + if (this.take()[0] < 0) { + for (int index = 0; index < bytes.length; index += 1) { + final int zeros = BytesRaw.numberOfLeadingZeros( + bytes[index] + ); + bytes[index] = (byte) (bytes[index] & 0xFF ^ (-1 << (Byte.SIZE - zeros))); + if (zeros != Byte.SIZE) { + break; + } + } + } + return new BytesOf(bytes); + } + + @Override + public T asNumber(final Class type) { + final byte[] ret = this.take(); + final Object res; + final ByteBuffer buf = ByteBuffer.wrap(ret); + if (Long.class.equals(type) + && ret.length == Long.BYTES) { + res = buf.getLong(); + } else if (Integer.class.equals(type) + && ret.length == Integer.BYTES + ) { + res = buf.getInt(); + } else if (Double.class.equals(type) + && ret.length == Double.BYTES + ) { + res = buf.getDouble(); + } else { + throw new UnsupportedOperationException( + String.format( + "Unsupported conversion to '%s' for %d bytes", + type, + ret.length + ) + ); + } + return type.cast(res); + } + + @Override + public String asString() { + final StringBuilder out = new StringBuilder(0); + for (final byte bte : this.data) { + if (out.length() > 0) { + out.append('-'); + } + out.append(String.format("%02X", bte)); + } + if (this.data.length == 0) { + out.append("--"); + } + return out.toString(); + } + + @Override + public byte[] take() { + return Arrays.copyOf(this.data, this.data.length); + } + + @Override + public String toString() { + return String.format("BytesOf{%s}", Arrays.toString(this.data)); + } + + @Override + public boolean equals(final Object other) { + final boolean result; + if (this == other) { + result = true; + } else if (other instanceof Bytes) { + result = Arrays.equals(this.data, ((Bytes) other).take()); + } else { + result = false; + } + return result; + } + + @Override + public int hashCode() { + return Arrays.hashCode(this.data); + } + + /** + * Count leading zero bits. + * @param num Byte. + * @return Number between 0 and 8. + */ + private static byte numberOfLeadingZeros(final byte num) { + final byte result; + if (num == 0) { + result = (byte) Byte.SIZE; + } else if (num < 0) { + result = (byte) 0; + } else { + byte temp = num; + int bts = Byte.SIZE - 1; + if (temp >= 1 << 4) { + bts -= 4; + temp >>>= 4; + } + if (temp >= 1 << 2) { + bts -= 2; + temp >>>= 2; + } + result = (byte) (bts - (temp >>> 1)); + } + return result; + } +} From 26d8412d04f5146e2c325a5eaf8f77fcb7da7c7d Mon Sep 17 00:00:00 2001 From: levBagryansky <28lev11@gmail.com> Date: Tue, 9 Jul 2024 14:15:01 +0300 Subject: [PATCH 2/3] #3239: Added one more ctor --- eo-runtime/src/main/java/org/eolang/BytesOf.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/eo-runtime/src/main/java/org/eolang/BytesOf.java b/eo-runtime/src/main/java/org/eolang/BytesOf.java index d6187c6ec9..a1d895387c 100644 --- a/eo-runtime/src/main/java/org/eolang/BytesOf.java +++ b/eo-runtime/src/main/java/org/eolang/BytesOf.java @@ -41,12 +41,19 @@ public final class BytesOf implements Bytes { */ private final Bytes bytes; + /** + * Ctor. + * @param bytes Bytes. + */ + public BytesOf(final Bytes bytes) { + this.bytes = bytes; + } /** * Ctor. * @param data Data. */ public BytesOf(final byte[] data) { - this.bytes = new BytesRaw(Arrays.copyOf(data, data.length)); + this(new BytesRaw(Arrays.copyOf(data, data.length))); } /** From 255fe2238898ee052ccc3aca194779ebd00687d0 Mon Sep 17 00:00:00 2001 From: levBagryansky <28lev11@gmail.com> Date: Tue, 9 Jul 2024 14:17:44 +0300 Subject: [PATCH 3/3] #3239: pqulice violation --- eo-runtime/src/main/java/org/eolang/BytesOf.java | 1 + 1 file changed, 1 insertion(+) diff --git a/eo-runtime/src/main/java/org/eolang/BytesOf.java b/eo-runtime/src/main/java/org/eolang/BytesOf.java index a1d895387c..502a1b4909 100644 --- a/eo-runtime/src/main/java/org/eolang/BytesOf.java +++ b/eo-runtime/src/main/java/org/eolang/BytesOf.java @@ -48,6 +48,7 @@ public final class BytesOf implements Bytes { public BytesOf(final Bytes bytes) { this.bytes = bytes; } + /** * Ctor. * @param data Data.