diff --git a/core/src/main/java/dev/keva/core/command/impl/connection/Auth.java b/core/src/main/java/dev/keva/core/command/impl/connection/Auth.java index 5bbbc14a..869df01a 100644 --- a/core/src/main/java/dev/keva/core/command/impl/connection/Auth.java +++ b/core/src/main/java/dev/keva/core/command/impl/connection/Auth.java @@ -12,6 +12,8 @@ import dev.keva.protocol.resp.reply.StatusReply; import io.netty.channel.ChannelHandlerContext; +import java.nio.charset.StandardCharsets; + @Component @CommandImpl("auth") @ParamLength(1) @@ -27,7 +29,7 @@ public Auth(KevaConfig kevaConfig, AuthManager authManager) { @Execute public Reply execute(byte[] password, ChannelHandlerContext ctx) { - String passwordString = new String(password); + String passwordString = new String(password, StandardCharsets.UTF_8).intern(); if (kevaConfig.getPassword().equals(passwordString)) { authManager.authenticate(ctx.channel()); return new StatusReply("OK"); diff --git a/core/src/main/java/dev/keva/core/command/impl/connection/Client.java b/core/src/main/java/dev/keva/core/command/impl/connection/Client.java index 460ec749..0ccf1e88 100644 --- a/core/src/main/java/dev/keva/core/command/impl/connection/Client.java +++ b/core/src/main/java/dev/keva/core/command/impl/connection/Client.java @@ -9,13 +9,15 @@ import dev.keva.protocol.resp.reply.Reply; import io.netty.channel.ChannelHandlerContext; +import java.nio.charset.StandardCharsets; + @Component @CommandImpl("client") @ParamLength(1) public class Client { @Execute public Reply execute(byte[] param, ChannelHandlerContext ctx) { - String paramStr = new String(param); + String paramStr = new String(param, StandardCharsets.UTF_8).intern(); if (paramStr.equalsIgnoreCase("id")) { return new BulkReply(ctx.channel().id().asShortText()); } else if (paramStr.equalsIgnoreCase("info")) { diff --git a/core/src/main/java/dev/keva/core/command/impl/hash/HSet.java b/core/src/main/java/dev/keva/core/command/impl/hash/HSet.java index 026b81f9..b2b59d2e 100644 --- a/core/src/main/java/dev/keva/core/command/impl/hash/HSet.java +++ b/core/src/main/java/dev/keva/core/command/impl/hash/HSet.java @@ -9,6 +9,8 @@ import dev.keva.protocol.resp.reply.IntegerReply; import dev.keva.store.KevaDatabase; +import java.nio.charset.StandardCharsets; + @Component @CommandImpl("hset") @ParamLength(type = ParamLength.Type.AT_LEAST, value = 3) @@ -28,7 +30,7 @@ public IntegerReply execute(byte[][] params) { if (params[i] == null) { args[i] = null; } else { - args[i] = new String(params[i]); + args[i] = new String(params[i], StandardCharsets.UTF_8).intern(); } } if (args[0] == null) { diff --git a/core/src/main/java/dev/keva/core/command/impl/key/Expire.java b/core/src/main/java/dev/keva/core/command/impl/key/Expire.java index 6bf9ec32..e7b08130 100644 --- a/core/src/main/java/dev/keva/core/command/impl/key/Expire.java +++ b/core/src/main/java/dev/keva/core/command/impl/key/Expire.java @@ -26,7 +26,7 @@ public Expire(ExpirationManager expirationManager) { @Execute public IntegerReply execute(byte[] key, byte[] after) { try { - val afterInMillis = Long.parseLong(new String(after, StandardCharsets.UTF_8)); + val afterInMillis = Long.parseLong(new String(after, StandardCharsets.UTF_8).intern()); expirationManager.expireAfter(key, afterInMillis); return new IntegerReply(1); } catch (Exception ignore) { diff --git a/core/src/main/java/dev/keva/core/command/impl/key/ExpireAt.java b/core/src/main/java/dev/keva/core/command/impl/key/ExpireAt.java index 5c0b83f2..1216aabe 100644 --- a/core/src/main/java/dev/keva/core/command/impl/key/ExpireAt.java +++ b/core/src/main/java/dev/keva/core/command/impl/key/ExpireAt.java @@ -26,7 +26,7 @@ public ExpireAt(ExpirationManager expirationManager) { @Execute public IntegerReply execute(byte[] key, byte[] at) { try { - val atInMillis = Long.parseLong(new String(at, StandardCharsets.UTF_8)); + val atInMillis = Long.parseLong(new String(at, StandardCharsets.UTF_8).intern()); expirationManager.expireAt(key, atInMillis); return new IntegerReply(1); } catch (Exception ignore) { diff --git a/core/src/main/java/dev/keva/core/command/impl/key/Incr.java b/core/src/main/java/dev/keva/core/command/impl/key/Incr.java index bc37d037..1455b785 100644 --- a/core/src/main/java/dev/keva/core/command/impl/key/Incr.java +++ b/core/src/main/java/dev/keva/core/command/impl/key/Incr.java @@ -34,6 +34,6 @@ public IntegerReply execute(byte[] key) { } catch (NumberFormatException ex) { throw new CommandException("Failed to parse integer from value stored"); } - return new IntegerReply(Long.parseLong(new String(newVal, StandardCharsets.UTF_8))); + return new IntegerReply(Long.parseLong(new String(newVal, StandardCharsets.UTF_8).intern())); } } diff --git a/core/src/main/java/dev/keva/core/command/impl/key/Incrby.java b/core/src/main/java/dev/keva/core/command/impl/key/Incrby.java index 9984e5a3..2fdc00c7 100644 --- a/core/src/main/java/dev/keva/core/command/impl/key/Incrby.java +++ b/core/src/main/java/dev/keva/core/command/impl/key/Incrby.java @@ -30,13 +30,13 @@ public Incrby(KevaDatabase database) { @Execute public IntegerReply execute(byte[] key, byte[] incrBy) { - var amount = Long.parseLong(new String(incrBy, StandardCharsets.UTF_8)); + var amount = Long.parseLong(new String(incrBy, StandardCharsets.UTF_8).intern()); byte[] newVal; try { newVal = database.incrBy(key, amount); } catch (NumberFormatException ex) { throw new CommandException("Failed to parse integer from value stored"); } - return new IntegerReply(Long.parseLong(new String(newVal, StandardCharsets.UTF_8))); + return new IntegerReply(Long.parseLong(new String(newVal, StandardCharsets.UTF_8).intern())); } } diff --git a/core/src/main/java/dev/keva/core/command/impl/key/Restore.java b/core/src/main/java/dev/keva/core/command/impl/key/Restore.java index 5c87a157..acd6a9bf 100644 --- a/core/src/main/java/dev/keva/core/command/impl/key/Restore.java +++ b/core/src/main/java/dev/keva/core/command/impl/key/Restore.java @@ -13,6 +13,7 @@ import lombok.val; import java.math.BigInteger; +import java.nio.charset.StandardCharsets; import java.util.Base64; @Component @@ -31,7 +32,7 @@ public Restore(KevaDatabase database, ExpirationManager expirationManager) { @Execute public Reply execute(byte[] key, byte[] ttl, byte[] dump, byte[] replace) { val old = database.get(key); - boolean isReplace = replace != null && new String(replace).equalsIgnoreCase("REPLACE"); + boolean isReplace = replace != null && new String(replace, StandardCharsets.UTF_8).intern().equalsIgnoreCase("REPLACE"); if (old != null && !isReplace) { return new ErrorReply("ERR Target key name is busy"); } diff --git a/core/src/main/java/dev/keva/core/command/impl/list/LIndex.java b/core/src/main/java/dev/keva/core/command/impl/list/LIndex.java index 1fc55697..f699d234 100644 --- a/core/src/main/java/dev/keva/core/command/impl/list/LIndex.java +++ b/core/src/main/java/dev/keva/core/command/impl/list/LIndex.java @@ -9,6 +9,8 @@ import dev.keva.store.KevaDatabase; import lombok.val; +import java.nio.charset.StandardCharsets; + @Component @CommandImpl("lindex") @ParamLength(2) @@ -22,7 +24,7 @@ public LIndex(KevaDatabase database) { @Execute public BulkReply execute(byte[] key, byte[] index) { - val got = database.lindex(key, Integer.parseInt(new String(index))); + val got = database.lindex(key, Integer.parseInt(new String(index).intern())); return got == null ? BulkReply.NIL_REPLY : new BulkReply(got); } } diff --git a/core/src/main/java/dev/keva/core/command/impl/list/LPop.java b/core/src/main/java/dev/keva/core/command/impl/list/LPop.java index 8f8174d6..2bc4f009 100644 --- a/core/src/main/java/dev/keva/core/command/impl/list/LPop.java +++ b/core/src/main/java/dev/keva/core/command/impl/list/LPop.java @@ -31,7 +31,7 @@ public Reply execute(byte[] key, byte[] count) { return got == null ? BulkReply.NIL_REPLY : new BulkReply(got); } - int countInt = Integer.parseInt(new String(count)); + int countInt = Integer.parseInt(new String(count).intern()); Reply[] replies = new Reply[countInt]; for (int i = 0; i < countInt; i++) { val got = database.lpop(key); diff --git a/core/src/main/java/dev/keva/core/command/impl/list/LRange.java b/core/src/main/java/dev/keva/core/command/impl/list/LRange.java index 7fd4f487..c37b19f1 100644 --- a/core/src/main/java/dev/keva/core/command/impl/list/LRange.java +++ b/core/src/main/java/dev/keva/core/command/impl/list/LRange.java @@ -24,7 +24,9 @@ public LRange(KevaDatabase database) { @Execute public Reply execute(byte[] key, byte[] start, byte[] stop) { - val got = database.lrange(key, Integer.parseInt(new String(start)), Integer.parseInt(new String(stop))); + val got = database.lrange(key, + Integer.parseInt(new String(start).intern()), + Integer.parseInt(new String(stop).intern())); if (got == null) { return MultiBulkReply.EMPTY; } diff --git a/core/src/main/java/dev/keva/core/command/impl/list/LRem.java b/core/src/main/java/dev/keva/core/command/impl/list/LRem.java index f66e2b66..4b3c94f1 100644 --- a/core/src/main/java/dev/keva/core/command/impl/list/LRem.java +++ b/core/src/main/java/dev/keva/core/command/impl/list/LRem.java @@ -23,7 +23,7 @@ public LRem(KevaDatabase database) { @Execute public IntegerReply execute(byte[] key, byte[] count, byte[] value) { - int result = database.lrem(key, Integer.parseInt(new String(count)), value); + int result = database.lrem(key, Integer.parseInt(new String(count).intern()), value); return new IntegerReply(result); } } diff --git a/core/src/main/java/dev/keva/core/command/impl/list/LSet.java b/core/src/main/java/dev/keva/core/command/impl/list/LSet.java index 36a2c202..4b43dd79 100644 --- a/core/src/main/java/dev/keva/core/command/impl/list/LSet.java +++ b/core/src/main/java/dev/keva/core/command/impl/list/LSet.java @@ -23,7 +23,7 @@ public LSet(KevaDatabase database) { @Execute public StatusReply execute(byte[] key, byte[] index, byte[] value) { - database.lset(key, Integer.parseInt(new String(index)), value); + database.lset(key, Integer.parseInt(new String(index).intern()), value); return StatusReply.OK; } } diff --git a/core/src/main/java/dev/keva/core/command/impl/list/RPop.java b/core/src/main/java/dev/keva/core/command/impl/list/RPop.java index 60957628..d75ca0ea 100644 --- a/core/src/main/java/dev/keva/core/command/impl/list/RPop.java +++ b/core/src/main/java/dev/keva/core/command/impl/list/RPop.java @@ -31,7 +31,7 @@ public Reply execute(byte[] key, byte[] count) { return got == null ? BulkReply.NIL_REPLY : new BulkReply(got); } - int countInt = Integer.parseInt(new String(count)); + int countInt = Integer.parseInt(new String(count).intern()); Reply[] replies = new Reply[countInt]; for (int i = 0; i < countInt; i++) { val got = database.rpop(key); diff --git a/core/src/main/java/dev/keva/core/command/impl/pubsub/Publish.java b/core/src/main/java/dev/keva/core/command/impl/pubsub/Publish.java index b9100101..d7b25806 100644 --- a/core/src/main/java/dev/keva/core/command/impl/pubsub/Publish.java +++ b/core/src/main/java/dev/keva/core/command/impl/pubsub/Publish.java @@ -14,6 +14,7 @@ import lombok.val; import lombok.var; +import java.nio.charset.StandardCharsets; import java.util.Set; @Component @@ -30,7 +31,7 @@ public Publish(PubSubManager manager) { @Execute public IntegerReply execute(byte[] topicBytes, byte[] message) { var count = 0; - val topic = new String(topicBytes).toLowerCase(); + val topic = new String(topicBytes, StandardCharsets.UTF_8).intern().toLowerCase(); Set set = manager.getTopics().get(topic); if (set != null) { for (Channel channel : set) { diff --git a/core/src/main/java/dev/keva/core/command/impl/pubsub/Subscribe.java b/core/src/main/java/dev/keva/core/command/impl/pubsub/Subscribe.java index ef3532e6..82666b21 100644 --- a/core/src/main/java/dev/keva/core/command/impl/pubsub/Subscribe.java +++ b/core/src/main/java/dev/keva/core/command/impl/pubsub/Subscribe.java @@ -15,6 +15,7 @@ import lombok.val; import lombok.var; +import java.nio.charset.StandardCharsets; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -43,7 +44,7 @@ public void execute(ChannelHandlerContext ctx, byte[]... topicBytes) { String[] topicsToSubscribe = new String[topicBytes.length]; for (int i = 0; i < topicBytes.length; i++) { - topicsToSubscribe[i] = new String(topicBytes[i]); + topicsToSubscribe[i] = new String(topicBytes[i], StandardCharsets.UTF_8).intern(); } for (val topic : topicsToSubscribe) { diff --git a/core/src/main/java/dev/keva/core/command/impl/pubsub/Unsubscribe.java b/core/src/main/java/dev/keva/core/command/impl/pubsub/Unsubscribe.java index 9c292346..497e1333 100644 --- a/core/src/main/java/dev/keva/core/command/impl/pubsub/Unsubscribe.java +++ b/core/src/main/java/dev/keva/core/command/impl/pubsub/Unsubscribe.java @@ -14,6 +14,7 @@ import lombok.val; import lombok.var; +import java.nio.charset.StandardCharsets; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -59,7 +60,7 @@ public void execute(ChannelHandlerContext ctx, byte[]... topicBytes) { String[] topicsToUnsubscribe = new String[topicBytes.length]; for (int i = 0; i < topicBytes.length; i++) { - topicsToUnsubscribe[i] = new String(topicBytes[i]); + topicsToUnsubscribe[i] = new String(topicBytes[i], StandardCharsets.UTF_8).intern(); } for (val topic : topicsToUnsubscribe) { diff --git a/core/src/main/java/dev/keva/core/server/NettyChannelHandler.java b/core/src/main/java/dev/keva/core/server/NettyChannelHandler.java index 3ac50798..70b247dd 100644 --- a/core/src/main/java/dev/keva/core/server/NettyChannelHandler.java +++ b/core/src/main/java/dev/keva/core/server/NettyChannelHandler.java @@ -31,7 +31,7 @@ protected void channelRead0(ChannelHandlerContext ctx, Command command) throws I val commandWrapper = commandMapper.getMethods().get(new BytesKey(name)); Reply reply; if (commandWrapper == null) { - reply = new ErrorReply("ERR unknown command `" + new String(name) + "`"); + reply = new ErrorReply("ERR unknown command `" + new String(name).intern() + "`"); } else { reply = commandWrapper.execute(ctx, command); } diff --git a/resp-protocol/src/main/java/dev/keva/protocol/resp/Command.java b/resp-protocol/src/main/java/dev/keva/protocol/resp/Command.java index 60a81053..96e9d32f 100644 --- a/resp-protocol/src/main/java/dev/keva/protocol/resp/Command.java +++ b/resp-protocol/src/main/java/dev/keva/protocol/resp/Command.java @@ -36,7 +36,7 @@ public static Command newInstance(byte[][] objects, boolean inline) { Command command = RECYCLER.get(); if (inline) { byte[] objs = objects[0]; - String[] strings = new String(objs, StandardCharsets.UTF_8).trim().split("\\s+"); + String[] strings = new String(objs, StandardCharsets.UTF_8).intern().trim().split("\\s+"); objects = new byte[strings.length][]; for (int i = 0; i < strings.length; i++) { objects[i] = ByteUtil.getBytes(strings[i]); diff --git a/store/src/main/java/dev/keva/store/impl/OffHeapDatabaseImpl.java b/store/src/main/java/dev/keva/store/impl/OffHeapDatabaseImpl.java index d1cb266a..673d3995 100644 --- a/store/src/main/java/dev/keva/store/impl/OffHeapDatabaseImpl.java +++ b/store/src/main/java/dev/keva/store/impl/OffHeapDatabaseImpl.java @@ -95,7 +95,7 @@ public byte[] incrBy(byte[] key, long amount) { return chronicleMap.compute(key, (k, oldVal) -> { long curVal = 0L; if (oldVal != null) { - curVal = Long.parseLong(new String(oldVal, StandardCharsets.UTF_8)); + curVal = Long.parseLong(new String(oldVal, StandardCharsets.UTF_8).intern()); } curVal = curVal + amount; return Long.toString(curVal).getBytes(StandardCharsets.UTF_8); @@ -671,7 +671,7 @@ public int strlen(byte[] key) { if (value == null) { return 0; } - return new String(value, StandardCharsets.UTF_8).length(); + return new String(value, StandardCharsets.UTF_8).intern().length(); } finally { lock.unlock(); } @@ -681,7 +681,7 @@ public int strlen(byte[] key) { public int setrange(byte[] key, byte[] offset, byte[] val) { lock.lock(); try { - var offsetPosition = Integer.parseInt(new String(offset, StandardCharsets.UTF_8)); + var offsetPosition = Integer.parseInt(new String(offset, StandardCharsets.UTF_8).intern()); byte[] oldVal = chronicleMap.get(key); int newValLength = oldVal == null ? offsetPosition + val.length : Math.max(offsetPosition + val.length, oldVal.length); byte[] newVal = new byte[newValLength]; diff --git a/store/src/main/java/dev/keva/store/impl/OnHeapDatabaseImpl.java b/store/src/main/java/dev/keva/store/impl/OnHeapDatabaseImpl.java index 75f3ca81..d7f33fa4 100644 --- a/store/src/main/java/dev/keva/store/impl/OnHeapDatabaseImpl.java +++ b/store/src/main/java/dev/keva/store/impl/OnHeapDatabaseImpl.java @@ -629,7 +629,7 @@ public int strlen(byte[] key) { if (value == null) { return 0; } - return new String(value, StandardCharsets.UTF_8).length(); + return new String(value, StandardCharsets.UTF_8).intern().length(); } finally { lock.unlock(); } @@ -639,7 +639,7 @@ public int strlen(byte[] key) { public int setrange(byte[] key, byte[] offset, byte[] val) { lock.lock(); try { - var offsetPosition = Integer.parseInt(new String(offset, StandardCharsets.UTF_8)); + var offsetPosition = Integer.parseInt(new String(offset, StandardCharsets.UTF_8).intern()); byte[] oldVal = map.get(new BytesKey(key)).getBytes(); int newValLength = oldVal == null ? offsetPosition + val.length : Math.max(offsetPosition + val.length, oldVal.length); byte[] newVal = new byte[newValLength]; diff --git a/util/src/main/java/dev/keva/util/hashbytes/BytesValue.java b/util/src/main/java/dev/keva/util/hashbytes/BytesValue.java index 7f483e5e..5818f5b6 100644 --- a/util/src/main/java/dev/keva/util/hashbytes/BytesValue.java +++ b/util/src/main/java/dev/keva/util/hashbytes/BytesValue.java @@ -41,7 +41,7 @@ public boolean equals(Object o) { @Override public String toString() { - return new String(bytes); + return new String(bytes).intern(); } public byte[] getBytes() {