2016-08-25 5 views
2

Прямо сейчас у меня есть небольшая процедура, которая пытается эффективно сегментировать сообщение на части, а для вычисления требуется добавить символы отдельно к OutputStream, обычно BAOS, а затем делать что-то вроде byte[] packed = packData(baos) и вычислять размер из упакованного размера. Этот шаг упаковки необходим, потому что я теряю биты, когда я делаю baos.write(my5bitbyte).В Java есть встроенный или общий вид Stream, который позволяет выводить, скажем, 5-битные данные?

Так на стадии упаковки, я обычно что-то вроде этого:

  1. Возьмите немного набор из байтов baos.toByteArray()
  2. сделать новый бит набор для построения байт
  3. Из каждого байта, возьмите биты 0-4 и добавьте их очевидным образом к новому набору бит
  4. Сделайте byte[] из нового набора бит, отложив последнее до 7 бит последнего байта

Мой вопрос заключается в следующем:

Есть ли способ или такая вещь, как BitOutputStream? Или что-то подобное? То, как я сейчас это делаю, кажется довольно глупым, и я определенно мог бы быть умнее, но я задаюсь вопросом, не забываю ли я что-то, что уже существует.

Редактировать Осмотрев источник из ByteArrayOutputStream, кажется очевидным, что он может быть реализован в точно так же, по какому-тому BitArrayOutputStream, потому что все это просто byte[] инкапсулируется с некоторыми модными вещами, так что вы могли бы сделать boolean[]. Но я не думаю, что он существует, теперь, когда я смотрю в него дальше, и поэтому мой вопрос становится тогда ...

Так будет ли это разумным способом реализовать BitArrayOutputStream?

class FixedLengthBitArrayOutputStream extends OutputStream { 

    private boolean[][] buffer; 
    private final int originalLength; 
    private final int bitLength; 
    private int position = 0; 
    private int expansions = 0; 

    FixedLengthBitArrayOutputStream(short bitLength, short length) { 
     this.buffer = new boolean[length][bitLength]; 
     this.originalLength = length; 
     this.bitLength = bitLength; 
    } 

    private int limitBeforeExpansion(double factor) { 
     return Math.max(
       (int) Math.floor(factor * buffer.length), 
       (int) Math.floor((1 - Math.pow(factor, expansions + 1)) * buffer.length) 
     ); 
    } 

    private boolean needsExpansion() { 
     return position > limitBeforeExpansion(0.8); 
    } 

    private void expandIfNecessary() { 
     if (needsExpansion()) { 
      expansions++; 
      this.buffer = Arrays.copyOf(this.buffer, (int) Math.pow((double)this.originalLength, expansions + 1)); 
     } 
    } 

    public boolean[] bitValue(int number) throws IllegalStateException { 

     int remainder = number; 
     boolean[] bits = new boolean[this.bitLength]; 

     for (int i = this.bitLength - 1; i >= 0; i--) { 
      int power = (int) Math.pow(2, i + 1); 
      boolean value = remainder > power; 
      bits[i] = value; 
      if (value) { 
       remainder -= power; 
      } 
     } 

     if (remainder != 0) 
      throw new IllegalStateException("whoa"); 

     return bits; 
    } 

    @Override 
    public void write(int b) throws IOException, IllegalStateException { 
     expandIfNecessary(); 

     this.buffer[position] = bitValue(b); 
     position++; 
    } 

    public byte[] toByteArray() { 

     BitSet bitSet = new BitSet(this.position * this.bitLength); 

     for (int i = 0; i < position; i++) { 
      boolean[] bits = this.buffer[i]; 

      for (int j = 0; j < bits.length; j++) { 
       bitSet.set(i * bits.length + j , bits[j]); 
      } 
     } 

     return bitSet.toByteArray(); 
    } 
} 
+0

Нет, поскольку самый маленький блок различных протоколов, устройств, процессоров, баранов и т. Д. - это байт. Вы можете просто поместить байты с нулями, если не хотите, нужны все биты –

+0

Реальный вопрос: почему вы используете 5-битную кодировку. Баудо вышел с аппаратами Телекса 30 лет назад. – EJP

+1

Почему бы не использовать собственный фильтр FilterOutputStream, который будет обертывать ваш ByteArrayOutputStream и который будет буферизовать полученные байты, пока они не будут объединены вместе и записаны в BAOS. Метод close() будет заполнять последние оставшиеся биты, если они есть, записать их в BAOS и закрыть BAOS. –

ответ

1

Естественный подход был бы написать отдельный класс выходного потока, что цепочки (или компрессы) другой выходной поток (так же, как вы приковать писатель, буферизованные потоки и потоки небуферизованных).

Код может выглядеть примерно так. Я буферизую несколько бит, пока не достигнет полного байта и не записывает его в выходной поток. Я не тестировал его. Таким образом, он, вероятно, будет содержать ошибку или два.

class PackedBitsOutputStream { 

    private OutputStream outputStream; 
    private int numBufferedBits; 
    private byte bufferedBits; 

    PackedBitsOutputStream(OutputStream os) { 
     outputStream = os; 
    } 

    void writeBitSet(int data, int relevantBits) { 
     bufferedBits = (byte) (bufferedBits | (data << bufferedBits)); 
     numBufferedBits += relevantBits; 
     if (numBufferedBits >= 8) { 
      outputStream.write(bufferedBits); 
      numBufferedBits -= 8; 
      bufferedBits = (byte) (data >> (relevantBits - numBufferedBits)); 
     } 
    } 

    void flush() { 
     outputStream.write(bufferedBits); 
     bufferedBits = 0; 
     numBufferedBits = 0; 
     outputStream.flush(); 
    } 

    void close() { 
     flush(); 
     outputStream.close(); 
    } 
} 

Примечание: writeBitSet в настоящее время может написать 8 бит на один раз больше всего.