001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  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 */
017package org.apache.juneau.terminal.region;
018
019import java.io.*;
020
021/**
022 * A growing stream of raw terminal bytes, read by absolute byte offset.
023 *
024 * <p>
025 * The bytes are exactly what the producer's terminal wrote, escape sequences included.  The server never decodes
026 * them.
027 */
028public interface TerminalSource {
029
030   /** The largest number of bytes one {@link #read(long, int)} returns. */
031   int MAX_READ_BYTES = 256 * 1024;
032
033   /**
034    * Reads up to {@code maxBytes} bytes starting at {@code fromOffset}.
035    *
036    * <p>
037    * When {@code fromOffset} equals the end, the chunk is empty.  When it is past the end, the chunk is empty, its
038    * {@code next} is its {@code end}, and that is below {@code fromOffset}, which the endpoint answers with {@code 416}.  When it is no longer
039    * held, the chunk is {@code truncated} and starts at the oldest offset held.  When the backing store has
040    * disappeared, the chunk is empty and {@code gone}, with {@code next} and {@code end} equal to {@code fromOffset}.
041    *
042    * @param fromOffset The offset to read from, at least 0.
043    * @param maxBytes The most bytes to return, clamped to 0-{@link #MAX_READ_BYTES}.
044    * @return The chunk.
045    * @throws IOException If the backing store could not be read.
046    */
047   TerminalChunk read(long fromOffset, int maxBytes) throws IOException;
048
049   /**
050    * Copies every byte still held, oldest first, up to the end at the time of the call.
051    *
052    * <p>
053    * If the oldest bytes have been overwritten, the copy starts at the oldest offset held and the gap is skipped.
054    *
055    * @param out The stream to write to.
056    * @throws IOException If the source could not be read or the stream written.
057    */
058   default void copyTo(OutputStream out) throws IOException {
059      var from = 0L;
060      var stop = -1L;
061      while (true) {
062         var c = read(from, MAX_READ_BYTES);
063         if (stop < 0)
064            stop = c.end();
065         out.write(c.bytes());
066         if (c.gone() || c.bytes().length == 0 || c.next() >= stop)
067            return;
068         from = c.next();
069      }
070   }
071}