skipUpTo helpfully falls back from skip invocations which return zero to invoke a read into a buffer.

The current implementation has two inefficiencies which cause unexpected performance degradation in tight loops, especially when a small number of bytes are requested:

1. In many cases skipSafely is able to skip all the requested bytes, there's no need to allocate a read buffer
2. The caller only requests a few bytes are skipped. Calling `ByteStreams.skipFully(stream, 32)` always allocates a 8192 byte buffer.

Proposed solution:
Defer buffer allocation until it is needed, using the minimum of the requested size and BUFFER_SIZE. This reduces allocation pressure in all cases of small skip values, as well as streams that report nonzero bytes available (assuming skip returns a positive value).
if (r == -1) {
          return combineBuffers(bufs, totalLen);
        }
        off += r;
        totalLen += r;
      }
    }
    
    if (in.read() == -1) {
      
      return combineBuffers(bufs, MAX_ARRAY_LEN);
    } else {
      throw new OutOfMemoryError("input is too large to fit in a byte array");
    }
  }
  private static byte[] combineBuffers(Deque<byte[]> bufs, int totalLen) {
    byte[] result = new byte[totalLen];
    int remaining = totalLen;
    while (remaining > 0) {
      byte[] buf = bufs.removeFirst();
      int bytesToCopy = Math.min(remaining, buf.length);
      int resultOffset = totalLen - remaining;
      System.arraycopy(buf, 0, result, resultOffset, bytesToCopy);
      remaining -= bytesToCopy;
    }
    return result;
  }
  /**
   * Reads all bytes from an input stream into a byte array. Does not close the stream.
   *