Опасности/гарантии использования ByteArrayInputStream для правильной маркировки/сброса

Вопрос может быть общим, но я пытаюсь понять основные последствия здесь.

Я пытаюсь выполнить некоторую разработку байт-кода с использованием библиотеки BCEL, и часть рабочего процесса требует, чтобы я читал один и тот же файл байт-кода несколько раз (с самого начала). Поток следующий

// 1. Get Input Stream

// 2. Do some work

// 3. Finish

// 4. Do some other work.

На шаге 4 мне нужно будет сбросить метку или получить поток, как будто он с самого начала. Я знаю следующие варианты.

1) Оберните поток, используя BufferedInputStream - шанс получить "Сброс до недопустимой отметки" IOException

2) Оберните его с помощью ByteArrayInputStream - он всегда работает, хотя некоторые онлайн-исследования показывают, что это ошибочно?

3) Просто вызовите getInputStream(), если мне нужно снова прочитать из потока.

Я пытаюсь понять, какой вариант будет лучше для меня. Я не хочу использовать BufferedInputStream, потому что я понятия не имею, где вызывается последний mark, поэтому вызов reset для более высокой позиции отметки вызовет IOException. Я бы предпочел использовать ByteArrayInputStream, поскольку для меня требуется минимальное изменение кода, но может ли кто-нибудь подсказать, какой вариант № 2 или вариант № 3 будет лучше?

Я знаю, что реализации для mark() и reset() различны для ByteArrayInputStream и BufferedInputStream в JDK.

С Уважением


person ha9u63ar    schedule 20.11.2017    source источник
comment
так что в основном вы читаете файл File, который превращается в файл .class? почему бы не прочитать его один раз и не сохранить, например, в массиве байтов?   -  person Eugene    schedule 20.11.2017
comment
@Eugene Да, сэр, я уже делаю это - это работает, но я верю или считаю экспертом и надеюсь, что есть подвох, который я, возможно, не учел. Вы знаете что-нибудь?   -  person ha9u63ar    schedule 20.11.2017
comment
Я знаю только, что использование метки и сброса важно, когда вы не хотите читать весь входной поток; или вы хотите прочитать следующие пару байтов, чтобы знать, что делать дальше; в противном случае чтение его в массив является самым простым (и самым понятным для меня)   -  person Eugene    schedule 20.11.2017
comment
@Юджин хорошо, это помогает. Итак, вы говорите, что вызов reset() для ByteArrayInputStream должен правильно сбросить его обратно в позицию 0 (я имею в виду начало) потока, если я хочу перезапустить? то есть не должно быть никакого ошибочного поведения (потому что я пока ни с чем не сталкивался)?   -  person ha9u63ar    schedule 20.11.2017
comment
хорошо, reset сбросит его (если markSupported == true) на любую поставленную отметку, а не на ноль, это вы, вероятно, имели в виду. Но да, если это поддерживается   -  person Eugene    schedule 20.11.2017


Ответы (1)


Проблема mark/reset заключается не только в том, что вам нужно заранее знать максимальное количество данных, считываемых между этими вызовами, вы также должны знать, будет ли код, которому вы делегируете полномочия, использовать эту функцию для себя, отображая ваши отметить устаревшим. Код, использующий mark/reset, не может запомнить и восстановить предыдущую метку для вызывающего объекта.

Таким образом, хотя можно было бы решить максимальную проблему, указав максимальный общий размер файла readlimit, вы никогда не можете полагаться на рабочую метку при передаче InputStream произвольной библиотечной функции, которая явно не документирует, чтобы никогда не использовать mark/reset особенность внутри.

Кроме того, BufferedInputStream, получающий readlimit, соответствующий общему размеру файла, не будет более эффективным, чем ByteArrayInputStream, упаковывающий массив, содержащий весь файл, поскольку оба в конечном итоге поддерживают буфер одинакового размера.


Лучшим решением было бы прочитать весь файл класса в массив один раз и напрямую использовать массив, например. для кода под вашим контролем или когда у вас есть выбор относительно библиотеки (например, ClassReader ASM поддерживает использование массива байтов вместо InputStream).

Если вам нужно передать InputStream библиотечной функции, настаивающей на этом, например, BCEL, тогда при необходимости оберните массив байтов в ByteArrayInputStream, но создавайте новый ByteArrayInputStream каждый раз, когда вам нужно повторно анализировать файл класса. Построение нового ByteArrayInputStream ничего не стоит, так как это легкая оболочка и надежна, так как никоим образом не зависит от состояния более старого входного потока. Вы даже можете иметь несколько экземпляров ByteArrayInputStream, одновременно читающих один и тот же массив.

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

person Holger    schedule 20.11.2017
comment
На самом деле я оборачиваю содержимое файла (байт []) в ByteArrayInputStream и передаю это - минимальное изменение в API - это работает для меня. Похоже, и у вас, и у @Eugene есть это в ваших комментариях. Я доволен этим на данный момент. - person ha9u63ar; 20.11.2017