001/* 
002 * This file is part of the Kompics component model runtime.
003 *
004 * Copyright (C) 2009 Swedish Institute of Computer Science (SICS) 
005 * Copyright (C) 2009 Royal Institute of Technology (KTH)
006 *
007 * This program is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU General Public License
009 * as published by the Free Software Foundation; either version 2
010 * of the License, or (at your option) any later version.
011 *
012 * This program is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
015 * GNU General Public License for more details.
016 *
017 * You should have received a copy of the GNU General Public License
018 * along with this program; if not, write to the Free Software
019 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
020 */
021package se.sics.kompics.network.netty.serialization;
022
023import java.util.Optional;
024import com.google.common.primitives.Ints;
025import com.google.common.primitives.UnsignedBytes;
026import io.netty.buffer.ByteBuf;
027import java.net.InetAddress;
028import java.net.InetSocketAddress;
029import java.net.UnknownHostException;
030import java.util.ArrayList;
031import java.util.Arrays;
032import java.util.UUID;
033import se.sics.kompics.network.Address;
034import se.sics.kompics.network.Transport;
035import se.sics.kompics.network.netty.DirectMessage;
036import se.sics.kompics.network.netty.NettyAddress;
037
038/**
039 *
040 * @author Lars Kroll {@literal <[email protected]>}
041 */
042public abstract class SpecialSerializers {
043
044    public static class NullSerializer implements Serializer {
045
046        @Override
047        public int identifier() {
048            return 0;
049        }
050
051        @Override
052        public void toBinary(Object o, ByteBuf buf) {
053            // simply ignore input
054        }
055
056        @Override
057        public Object fromBinary(ByteBuf buf, Optional<Object> hint) {
058            return null;
059        }
060
061    }
062
063    public static class ByteSerializer implements Serializer {
064
065        @Override
066        public int identifier() {
067            return 1;
068        }
069
070        @Override
071        public void toBinary(Object o, ByteBuf buf) {
072            byte[] bytes = (byte[]) o;
073            int code = buf.ensureWritable(bytes.length + 4, true);
074            if (code == 1 || code == 3) {
075                Serializers.LOG.error(
076                        "ByteSerializer: Not enough space left on buffer to serialize " + bytes.length + " bytes.");
077                return;
078            }
079            buf.writeInt(bytes.length);
080            buf.writeBytes(bytes);
081        }
082
083        @Override
084        public Object fromBinary(ByteBuf buf, Optional<Object> hint) {
085            int length = buf.readInt();
086            byte[] bytes = new byte[length];
087            buf.readBytes(bytes);
088            return bytes;
089        }
090
091    }
092
093    public static class AddressSerializer implements Serializer {
094
095        public static final int BYTE_KEY_SIZE = 255;
096        public static final int INT_BYTE_SIZE = Integer.SIZE / 8;
097        public static final AddressSerializer INSTANCE = new AddressSerializer();
098
099        @Override
100        public int identifier() {
101            return 2;
102        }
103
104        @Override
105        public void toBinary(Object o, ByteBuf buf) {
106            Address addr = (Address) o;
107            if (addr == null) {
108                buf.writeInt(0); // simply put four 0 bytes since 0.0.0.0 is not a valid host ip
109                return;
110            }
111
112            socketToBinary(addr.asSocket(), buf);
113        }
114
115        @Override
116        public Object fromBinary(ByteBuf buf, Optional<Object> hint) {
117            byte[] ipBytes = new byte[4];
118            buf.readBytes(ipBytes);
119            if ((ipBytes[0] == 0) && (ipBytes[1] == 0) && (ipBytes[2] == 0) && (ipBytes[3] == 0)) {
120                return null; // IP 0.0.0.0 is not valid but null Address encoding
121            }
122            InetAddress ip;
123            try {
124                ip = InetAddress.getByAddress(ipBytes);
125            } catch (UnknownHostException ex) {
126                Serializers.LOG.error("AddressSerializer: Could not create InetAddress.", ex);
127                return null;
128            }
129            byte portUpper = buf.readByte();
130            byte portLower = buf.readByte();
131            int port = Ints.fromBytes((byte) 0, (byte) 0, portUpper, portLower);
132
133            return new NettyAddress(ip, port);
134        }
135
136        public void socketToBinary(InetSocketAddress isa, ByteBuf buf) {
137            buf.writeBytes(isa.getAddress().getAddress());
138            // Write ports as 2 bytes instead of 4
139            byte[] portBytes = Ints.toByteArray(isa.getPort());
140            buf.writeByte(portBytes[2]);
141            buf.writeByte(portBytes[3]);
142        }
143
144        public InetSocketAddress socketFromBinary(ByteBuf buf) {
145            byte[] ipBytes = new byte[4];
146            buf.readBytes(ipBytes);
147            InetAddress ip;
148            try {
149                ip = InetAddress.getByAddress(ipBytes);
150            } catch (UnknownHostException ex) {
151                Serializers.LOG.error("AddressSerializer: Could not create InetAddress.", ex);
152                return null;
153            }
154            byte portUpper = buf.readByte();
155            byte portLower = buf.readByte();
156            int port = Ints.fromBytes((byte) 0, (byte) 0, portUpper, portLower);
157            return new InetSocketAddress(ip, port);
158        }
159
160    }
161
162    public static class UUIDSerializer implements Serializer {
163
164        public static final UUIDSerializer INSTANCE = new UUIDSerializer();
165
166        @Override
167        public int identifier() {
168            return 6;
169        }
170
171        @Override
172        public void toBinary(Object o, ByteBuf buf) {
173            if (o instanceof UUID) {
174                UUID id = (UUID) o;
175                buf.writeLong(id.getMostSignificantBits());
176                buf.writeLong(id.getLeastSignificantBits());
177            }
178        }
179
180        @Override
181        public Object fromBinary(ByteBuf buf, Optional<Object> hint) {
182            return new UUID(buf.readLong(), buf.readLong());
183        }
184
185    }
186
187    public static abstract class MessageSerializationUtil {
188
189        public static void msgToBinary(DirectMessage msg, ByteBuf buf, boolean flag1, boolean flag2) {
190            BitBuffer bbuf = BitBuffer.create(flag1, flag2, // good that a byte has so many bits... can compress it more
191                                                            // if more protocols are necessary
192                    msg.getProtocol() == Transport.UDP, msg.getProtocol() == Transport.TCP,
193                    msg.getProtocol() == Transport.MULTICAST_UDP, msg.getProtocol() == Transport.UDT,
194                    msg.getProtocol() == Transport.LEDBAT);
195            byte[] bbufb = bbuf.finalise();
196            buf.writeBytes(bbufb);
197            // Addresses
198            AddressSerializer.INSTANCE.toBinary(msg.getSource(), buf);
199            AddressSerializer.INSTANCE.toBinary(msg.getDestination(), buf);
200        }
201
202        public static MessageFields msgFromBinary(ByteBuf buf) {
203            MessageFields fields = new MessageFields();
204
205            byte[] flagB = new byte[1];
206            buf.readBytes(flagB);
207            boolean[] flags = BitBuffer.extract(8, flagB);
208            fields.flag1 = flags[0];
209            fields.flag2 = flags[1];
210            if (flags[2]) {
211                fields.proto = Transport.UDP;
212            }
213            if (flags[3]) {
214                fields.proto = Transport.TCP;
215            }
216            if (flags[4]) {
217                fields.proto = Transport.MULTICAST_UDP;
218            }
219            if (flags[5]) {
220                fields.proto = Transport.UDT;
221            }
222            if (flags[6]) {
223                fields.proto = Transport.LEDBAT;
224            }
225
226            // Addresses
227            fields.src = (NettyAddress) AddressSerializer.INSTANCE.fromBinary(buf, Optional.empty());
228            fields.dst = (NettyAddress) AddressSerializer.INSTANCE.fromBinary(buf, Optional.empty());
229            fields.orig = fields.src;
230
231            return fields;
232        }
233
234        public static class MessageFields {
235
236            public NettyAddress src;
237            public NettyAddress dst;
238            public NettyAddress orig;
239            public Transport proto;
240            public boolean flag1;
241            public boolean flag2;
242        }
243    }
244
245    static class BitBuffer {
246
247        private static final int ZERO = 0;
248        private static final int[] POS = { 1, 2, 4, 8, 16, 32, 64, 128 };
249
250        private final ArrayList<Boolean> buffer = new ArrayList<Boolean>();
251
252        private BitBuffer() {
253        }
254
255        public static BitBuffer create(Boolean... args) {
256            BitBuffer b = new BitBuffer();
257            b.buffer.addAll(Arrays.asList(args));
258            return b;
259        }
260
261        public BitBuffer write(Boolean... args) {
262            buffer.addAll(Arrays.asList(args));
263            return this;
264        }
265
266        public byte[] finalise() {
267            int numBytes = (int) Math.ceil(((double) buffer.size()) / 8.0);
268            byte[] bytes = new byte[numBytes];
269            for (int i = 0; i < numBytes; i++) {
270                int b = ZERO;
271                for (int j = 0; j < 8; j++) {
272                    int pos = i * 8 + j;
273                    if (buffer.size() > pos) {
274                        if (buffer.get(pos)) {
275                            b = b ^ POS[j];
276                        }
277                    }
278                }
279                bytes[i] = UnsignedBytes.checkedCast(b);
280            }
281            return bytes;
282        }
283
284        public static boolean[] extract(int numValues, byte[] bytes) {
285            assert (((int) Math.ceil(((double) numValues) / 8.0)) <= bytes.length);
286
287            boolean[] output = new boolean[numValues];
288            for (int i = 0; i < bytes.length; i++) {
289                int b = bytes[i];
290                for (int j = 0; j < 8; j++) {
291                    int pos = i * 8 + j;
292                    if (pos >= numValues) {
293                        return output;
294                    }
295                    output[pos] = ((b & POS[j]) != 0);
296                }
297            }
298
299            return output;
300        }
301    }
302}