001/* 002 * Trident - A Multithreaded Server Alternative 003 * Copyright 2014 The TridentSDK Team 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package net.tridentsdk.server.netty; 019 020import com.google.common.base.Charsets; 021import com.google.common.base.Preconditions; 022import io.netty.buffer.ByteBuf; 023 024import javax.annotation.concurrent.ThreadSafe; 025import java.math.BigInteger; 026import java.nio.charset.Charset; 027 028/** 029 * Utility class to help decode the bytes from a backed buffer serializer 030 * 031 * @author The TridentSDK Team 032 */ 033@ThreadSafe 034public final class Codec { 035 //Current charset used by strings is UTF_8 036 /** 037 * The charset used for Strings 038 */ 039 public static final Charset CHARSET = Charsets.UTF_8; 040 041 private Codec() { 042 } // Suppress initialization of utility class 043 044 /** 045 * Read a string from the encoded buffer 046 * 047 * @param buf the buffer to decode the string from 048 * @return the decoded string read from the buffer 049 */ 050 public static String readString(ByteBuf buf) { 051 //Reads the length of the string 052 int length = readVarInt32(buf); 053 byte[] bytes = new byte[length]; 054 buf.readBytes(bytes); 055 056 return new String(bytes, CHARSET); 057 } 058 059 /** 060 * Gets the byte length of provided integer 061 * 062 * @param integer integer to get the byte length of 063 * @return byte length of the integer 064 */ 065 public static int sizeOf(int integer) { 066 return BigInteger.valueOf(integer).toByteArray().length; 067 } 068 069 /** 070 * Writes a string to the buffer 071 * 072 * @param buf the buffer to decode the string from 073 */ 074 public static void writeString(ByteBuf buf, String string) { 075 if (string == null) { 076 return; 077 } 078 //Writes the length of the string 079 writeVarInt32(buf, string.length()); 080 081 //Writes the bytes of the string 082 byte[] bytes = string.getBytes(CHARSET); 083 buf.writeBytes(bytes); 084 } 085 086 /** 087 * Reads a 32bit VarInt from the encoded buffer 088 * 089 * @param buf the buffer to decode the integer from 090 * @return the decoded integer read from the buffer 091 */ 092 public static int readVarInt32(ByteBuf buf) { 093 //The result we will return 094 int result = 0; 095 096 //How much to indent the current bytes 097 int indent = 0; 098 int b = (int) buf.readByte(); 099 100 //If below, it means there are more bytes 101 // 0x80 = 128 for those that don't know 102 while ((b & 0x80) == 0x80) { 103 Preconditions.checkArgument(indent < 21, "Too many bytes for a VarInt32."); 104 105 //Adds the byte in the appropriate position (first byte goes last, etc.) 106 result += (b & 0x7f) << indent; 107 indent += 7; 108 109 //Reads the next byte 110 b = (int) buf.readByte(); 111 } 112 113 // 0x7f = 127 114 result += (b & 0x7f) << indent; 115 return result; 116 } 117 118 /** 119 * Writes an int value as a VarInt to the buffer. 120 * 121 * @param buf the buffer to encode into 122 * @param toEncode the integer encode into buf 123 */ 124 public static void writeVarInt32(ByteBuf buf, int toEncode) { 125 //Loops through until the currently 'selected' set of 7 bits is the terminating one 126 while ((toEncode & 0xFFFFFF80) != 0L) { 127 /*Writes the selected 7 bits, and adds a 1 at the front 128 signifying that there is another byte*/ 129 buf.writeByte(toEncode & 0x7F | 0x80); 130 //Selects the next set of 7 bits 131 toEncode >>>= 7; 132 } 133 //Writes the final terminating byte with a 0 at the front to signify termination 134 buf.writeByte(toEncode & 0x7F); 135 } 136 137 /** 138 * Reads a 64bit VarInt from the encoded buffer 139 * 140 * @param buf the buffer to decode the long from 141 * @return the decoded long read from the buffer 142 */ 143 public static long readVarInt64(ByteBuf buf) { 144 //The result we will return 145 long result = 0L; 146 147 //How much to indent the current bytes 148 int indent = 0; 149 long b = (long) buf.readByte(); 150 151 //If below, it means there are more bytes 152 while ((b & 0x80L) == 0x80) { 153 Preconditions.checkArgument(indent < 49, "Too many bytes for a VarInt64."); 154 155 //Adds the byte in the appropriate position (first byte goes last, etc.) 156 result += (b & 0x7fL) << indent; 157 indent += 7; 158 159 //Reads the next byte 160 b = (long) buf.readByte(); 161 } 162 163 return result += (b & 0x7fL) << indent; 164 } 165 166 /** 167 * Writes a long value as a VarInt to the buffer. 168 * 169 * @param buf the buffer to encode into 170 * @param toEncode the integer encode into buf 171 */ 172 public static void writeVarInt64(ByteBuf buf, long toEncode) { 173 //Loops through until the currently 'selected' set of 7 bits is the terminating one 174 while ((toEncode & 0xFFFFFFFFFFFFFF80L) != 0L) { 175 /*Writes the selected 7 bits, and adds a 1 at the front 176 signifying that there is another byte*/ 177 buf.writeByte((int) (toEncode & 0x7FL | 0x80L)); 178 //Selects the next set of 7 bits 179 toEncode >>>= 7L; 180 } 181 //Writes the final terminating byte with a 0 at the front to signify termination 182 buf.writeByte((int) (toEncode & 0x7FL)); 183 } 184 185 /** 186 * Writes the full contents of a ByteBuf to an array 187 * 188 * @param buf the buffer to get data from 189 * @return bytes the array of bytes 190 */ 191 public static byte[] asArray(ByteBuf buf) { 192 return asArray(buf, buf.readableBytes()); 193 } 194 195 /** 196 * Writes a certain length of bytes from a ByteBuf to an array 197 * 198 * @param buf the buffer to get data from 199 * @param length the length to toPacket 200 * @return bytes the array of bytes 201 */ 202 public static byte[] asArray(ByteBuf buf, int length) { 203 byte[] bytes = new byte[length]; 204 buf.getBytes(buf.readerIndex(), bytes); 205 return bytes; 206 } 207}