Реализация собственных Input/Output Stream
от heavenzip
В Java очень богатый набор классов по работе с потоками: InputStream, DataInputStream, ByteArrayOutputStream, PrintStream и т.д. Каждый из них полезен в той или иной ситуации. Поэтому важно уметь работать с ними.
Например, нужно в игре защитить графику. Можно считать весь файл в массив, провести над ним операции по раскодированию и потом из массива байт создать картинку. Такой способ не очень хорош из-за повышенного потребления памяти и лишних операций. А почему бы нам сразу не читать байты уже раскодированными? Вот тут нам и поможет возможность создания своего Input/OutputStream'а.
Начнём с OutputStream. Первым делом, нужно создать свой класс, наследуемый от OutputStream. На вход он будет принимать существующий OutputStream.
Для примера взят простейший метод шифрования, XOR, но при желании можно усложнить алгоритм.
Как видно, мы просто обернули переданный в конструктор OutputStream.
InputStream создаётся тем же способом. Учтите, алгоритм шифрования должен быть в этом случае обратный. Если в OutputStream мы прибавляли к каждому байту 5, то в InputStream нужно отнять 5 и т.д. В случае с XOR ничего делать не надо, он сам по себе инверсный.
Теперь проверим.
Запишем какие-нибудь данные в массив байт
В закодированном виде это выглядит так:
101, 110, 45, 0, 9, 9, 10, 69, 18, 10, 23, 9, 1, 101, -22, 69, -3, 100, 90, -86, 127, -59
В обычном так:
0, 11, 72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 0, -113, 32, -104, 1, 63, -49, 26, -96
Также, теперь очень легко загружать зашифрованные картинки из файла/ресурсов/интернета
Помимо шифрования, можно добавить какие-нибудь вспомогательные методы, например побитовое чтение.
Выведет
01110100011001010111001101110100
Исходники + бинарники: Java ME | Java SE
Например, нужно в игре защитить графику. Можно считать весь файл в массив, провести над ним операции по раскодированию и потом из массива байт создать картинку. Такой способ не очень хорош из-за повышенного потребления памяти и лишних операций. А почему бы нам сразу не читать байты уже раскодированными? Вот тут нам и поможет возможность создания своего Input/OutputStream'а.
Начнём с OutputStream. Первым делом, нужно создать свой класс, наследуемый от OutputStream. На вход он будет принимать существующий OutputStream.
- public class EncryptedOutputStream extends OutputStream {
- protected OutputStream os;
- private byte key;
- public EncryptedOutputStream(OutputStream os, byte key) {
- this.os = os;
- this.key = key;
- }
- public void write(int b) throws IOException {
- os.write(b ^ key);
- }
- // ...
- public void flush() throws IOException {
- os.flush();
- }
- public void close() throws IOException {
- os.close();
- }
- }
Для примера взят простейший метод шифрования, XOR, но при желании можно усложнить алгоритм.
Как видно, мы просто обернули переданный в конструктор OutputStream.
InputStream создаётся тем же способом. Учтите, алгоритм шифрования должен быть в этом случае обратный. Если в OutputStream мы прибавляли к каждому байту 5, то в InputStream нужно отнять 5 и т.д. В случае с XOR ничего делать не надо, он сам по себе инверсный.
- public class EncryptedInputStream extends InputStream {
- protected InputStream is;
- private byte key;
- public EncryptedInputStream(InputStream is, byte key) {
- this.is = is;
- this.key = key;
- }
- public int read() throws IOException {
- return is.read() ^ key;
- }
- // ...
- public void close() throws IOException {
- is.close();
- }
- public synchronized void mark(int readlimit) {
- is.mark(readlimit);
- }
- }
Теперь проверим.
Запишем какие-нибудь данные в массив байт
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- DataOutputStream dos = new DataOutputStream(new EncryptedOutputStream(baos, (byte) 101));
- dos.writeUTF("Hello world");
- dos.writeInt(9379992);
- dos.writeBoolean(true);
- dos.writeFloat(1.618f);
- dos.flush();
- byte[] data = baos.toByteArray();
- baos.close();
В закодированном виде это выглядит так:
101, 110, 45, 0, 9, 9, 10, 69, 18, 10, 23, 9, 1, 101, -22, 69, -3, 100, 90, -86, 127, -59
В обычном так:
0, 11, 72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 0, -113, 32, -104, 1, 63, -49, 26, -96
Также, теперь очень легко загружать зашифрованные картинки из файла/ресурсов/интернета
- InputStream is = getClass().getResourceAsStream(path);
- EncryptedInputStream eis = new EncryptedInputStream(is, (byte) 0x77);
- Image img = Image.createImage(eis);
Помимо шифрования, можно добавить какие-нибудь вспомогательные методы, например побитовое чтение.
- public class BitInputStream extends InputStream {
- protected InputStream is;
- private int bitshift = -1;
- private int bits;
- public BitInputStream(InputStream is) {
- this.is = is;
- }
- public boolean readBit() throws IOException {
- if (bitshift == -1) {
- bits = read() & 0xFF;
- bitshift = 7;
- }
- boolean bit = (((bits >> bitshift) & 1) != 0);
- bitshift--;
- return bit;
- }
- public int read() throws IOException {
- return is.read();
- }
- // ...
- }
- ByteArrayInputStream bais = new ByteArrayInputStream("test".getBytes());
- BitInputStream bis = new BitInputStream(bais);
- int bitCount = bis.available() * 8;
- for (int i = 0; i < bitCount; i++) {
- System.out.print(bis.readBit() ? '1' : '0');
- }
- bis.close();
Выведет
01110100011001010111001101110100
Исходники + бинарники: Java ME | Java SE