/* * Copyright 2006 The Android Open Source Project * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #ifndef SkStream_DEFINED #define SkStream_DEFINED #include "include/core/SkData.h" #include "include/core/SkRefCnt.h" #include "include/core/SkScalar.h" #include "include/private/SkTo.h" #include class SkStream; class SkStreamRewindable; class SkStreamSeekable; class SkStreamAsset; class SkStreamMemory; /** * SkStream -- abstraction for a source of bytes. Subclasses can be backed by * memory, or a file, or something else. * * NOTE: * * Classic "streams" APIs are sort of async, in that on a request for N * bytes, they may return fewer than N bytes on a given call, in which case * the caller can "try again" to get more bytes, eventually (modulo an error) * receiving their total N bytes. * * Skia streams behave differently. They are effectively synchronous, and will * always return all N bytes of the request if possible. If they return fewer * (the read() call returns the number of bytes read) then that means there is * no more data (at EOF or hit an error). The caller should *not* call again * in hopes of fulfilling more of the request. */ class SK_API SkStream { public: virtual ~SkStream() {} SkStream() {} /** * Attempts to open the specified file as a stream, returns nullptr on failure. */ static std::unique_ptr MakeFromFile(const char path[]); /** Reads or skips size number of bytes. * If buffer == NULL, skip size bytes, return how many were skipped. * If buffer != NULL, copy size bytes into buffer, return how many were copied. * @param buffer when NULL skip size bytes, otherwise copy size bytes into buffer * @param size the number of bytes to skip or copy * @return the number of bytes actually read. */ virtual size_t read(void* buffer, size_t size) = 0; /** Skip size number of bytes. * @return the actual number bytes that could be skipped. */ size_t skip(size_t size) { return this->read(nullptr, size); } /** * Attempt to peek at size bytes. * If this stream supports peeking, copy min(size, peekable bytes) into * buffer, and return the number of bytes copied. * If the stream does not support peeking, or cannot peek any bytes, * return 0 and leave buffer unchanged. * The stream is guaranteed to be in the same visible state after this * call, regardless of success or failure. * @param buffer Must not be NULL, and must be at least size bytes. Destination * to copy bytes. * @param size Number of bytes to copy. * @return The number of bytes peeked/copied. */ virtual size_t peek(void* /*buffer*/, size_t /*size*/) const { return 0; } /** Returns true when all the bytes in the stream have been read. * This may return true early (when there are no more bytes to be read) * or late (after the first unsuccessful read). */ virtual bool isAtEnd() const = 0; bool SK_WARN_UNUSED_RESULT readS8(int8_t*); bool SK_WARN_UNUSED_RESULT readS16(int16_t*); bool SK_WARN_UNUSED_RESULT readS32(int32_t*); bool SK_WARN_UNUSED_RESULT readU8(uint8_t* i) { return this->readS8((int8_t*)i); } bool SK_WARN_UNUSED_RESULT readU16(uint16_t* i) { return this->readS16((int16_t*)i); } bool SK_WARN_UNUSED_RESULT readU32(uint32_t* i) { return this->readS32((int32_t*)i); } bool SK_WARN_UNUSED_RESULT readBool(bool* b) { uint8_t i; if (!this->readU8(&i)) { return false; } *b = (i != 0); return true; } bool SK_WARN_UNUSED_RESULT readScalar(SkScalar*); bool SK_WARN_UNUSED_RESULT readPackedUInt(size_t*); //SkStreamRewindable /** Rewinds to the beginning of the stream. Returns true if the stream is known * to be at the beginning after this call returns. */ virtual bool rewind() { return false; } /** Duplicates this stream. If this cannot be done, returns NULL. * The returned stream will be positioned at the beginning of its data. */ std::unique_ptr duplicate() const { return std::unique_ptr(this->onDuplicate()); } /** Duplicates this stream. If this cannot be done, returns NULL. * The returned stream will be positioned the same as this stream. */ std::unique_ptr fork() const { return std::unique_ptr(this->onFork()); } //SkStreamSeekable /** Returns true if this stream can report it's current position. */ virtual bool hasPosition() const { return false; } /** Returns the current position in the stream. If this cannot be done, returns 0. */ virtual size_t getPosition() const { return 0; } /** Seeks to an absolute position in the stream. If this cannot be done, returns false. * If an attempt is made to seek past the end of the stream, the position will be set * to the end of the stream. */ virtual bool seek(size_t /*position*/) { return false; } /** Seeks to an relative offset in the stream. If this cannot be done, returns false. * If an attempt is made to move to a position outside the stream, the position will be set * to the closest point within the stream (beginning or end). */ virtual bool move(long /*offset*/) { return false; } //SkStreamAsset /** Returns true if this stream can report it's total length. */ virtual bool hasLength() const { return false; } /** Returns the total length of the stream. If this cannot be done, returns 0. */ virtual size_t getLength() const { return 0; } //SkStreamMemory /** Returns the starting address for the data. If this cannot be done, returns NULL. */ //TODO: replace with virtual const SkData* getData() virtual const void* getMemoryBase() { return nullptr; } private: virtual SkStream* onDuplicate() const { return nullptr; } virtual SkStream* onFork() const { return nullptr; } SkStream(SkStream&&) = delete; SkStream(const SkStream&) = delete; SkStream& operator=(SkStream&&) = delete; SkStream& operator=(const SkStream&) = delete; }; /** SkStreamRewindable is a SkStream for which rewind and duplicate are required. */ class SK_API SkStreamRewindable : public SkStream { public: bool rewind() override = 0; std::unique_ptr duplicate() const { return std::unique_ptr(this->onDuplicate()); } private: SkStreamRewindable* onDuplicate() const override = 0; }; /** SkStreamSeekable is a SkStreamRewindable for which position, seek, move, and fork are required. */ class SK_API SkStreamSeekable : public SkStreamRewindable { public: std::unique_ptr duplicate() const { return std::unique_ptr(this->onDuplicate()); } bool hasPosition() const override { return true; } size_t getPosition() const override = 0; bool seek(size_t position) override = 0; bool move(long offset) override = 0; std::unique_ptr fork() const { return std::unique_ptr(this->onFork()); } private: SkStreamSeekable* onDuplicate() const override = 0; SkStreamSeekable* onFork() const override = 0; }; /** SkStreamAsset is a SkStreamSeekable for which getLength is required. */ class SK_API SkStreamAsset : public SkStreamSeekable { public: bool hasLength() const override { return true; } size_t getLength() const override = 0; std::unique_ptr duplicate() const { return std::unique_ptr(this->onDuplicate()); } std::unique_ptr fork() const { return std::unique_ptr(this->onFork()); } private: SkStreamAsset* onDuplicate() const override = 0; SkStreamAsset* onFork() const override = 0; }; /** SkStreamMemory is a SkStreamAsset for which getMemoryBase is required. */ class SK_API SkStreamMemory : public SkStreamAsset { public: const void* getMemoryBase() override = 0; std::unique_ptr duplicate() const { return std::unique_ptr(this->onDuplicate()); } std::unique_ptr fork() const { return std::unique_ptr(this->onFork()); } private: SkStreamMemory* onDuplicate() const override = 0; SkStreamMemory* onFork() const override = 0; }; class SK_API SkWStream { public: virtual ~SkWStream(); SkWStream() {} /** Called to write bytes to a SkWStream. Returns true on success @param buffer the address of at least size bytes to be written to the stream @param size The number of bytes in buffer to write to the stream @return true on success */ virtual bool write(const void* buffer, size_t size) = 0; virtual void flush(); virtual size_t bytesWritten() const = 0; // helpers bool write8(U8CPU value) { uint8_t v = SkToU8(value); return this->write(&v, 1); } bool write16(U16CPU value) { uint16_t v = SkToU16(value); return this->write(&v, 2); } bool write32(uint32_t v) { return this->write(&v, 4); } bool writeText(const char text[]) { SkASSERT(text); return this->write(text, strlen(text)); } bool newline() { return this->write("\n", strlen("\n")); } bool writeDecAsText(int32_t); bool writeBigDecAsText(int64_t, int minDigits = 0); bool writeHexAsText(uint32_t, int minDigits = 0); bool writeScalarAsText(SkScalar); bool writeBool(bool v) { return this->write8(v); } bool writeScalar(SkScalar); bool writePackedUInt(size_t); bool writeStream(SkStream* input, size_t length); /** * This returns the number of bytes in the stream required to store * 'value'. */ static int SizeOfPackedUInt(size_t value); private: SkWStream(const SkWStream&) = delete; SkWStream& operator=(const SkWStream&) = delete; }; class SK_API SkNullWStream : public SkWStream { public: SkNullWStream() : fBytesWritten(0) {} bool write(const void* , size_t n) override { fBytesWritten += n; return true; } void flush() override {} size_t bytesWritten() const override { return fBytesWritten; } private: size_t fBytesWritten; }; //////////////////////////////////////////////////////////////////////////////////////// #include /** A stream that wraps a C FILE* file stream. */ class SK_API SkFILEStream : public SkStreamAsset { public: /** Initialize the stream by calling sk_fopen on the specified path. * This internal stream will be closed in the destructor. */ explicit SkFILEStream(const char path[] = nullptr); /** Initialize the stream with an existing C FILE stream. * The current position of the C FILE stream will be considered the * beginning of the SkFILEStream and the current seek end of the FILE will be the end. * The C FILE stream will be closed in the destructor. */ explicit SkFILEStream(FILE* file); /** Initialize the stream with an existing C FILE stream. * The current position of the C FILE stream will be considered the * beginning of the SkFILEStream and size bytes later will be the end. * The C FILE stream will be closed in the destructor. */ explicit SkFILEStream(FILE* file, size_t size); ~SkFILEStream() override; static std::unique_ptr Make(const char path[]) { std::unique_ptr stream(new SkFILEStream(path)); return stream->isValid() ? std::move(stream) : nullptr; } /** Returns true if the current path could be opened. */ bool isValid() const { return fFILE != nullptr; } /** Close this SkFILEStream. */ void close(); size_t read(void* buffer, size_t size) override; bool isAtEnd() const override; bool rewind() override; std::unique_ptr duplicate() const { return std::unique_ptr(this->onDuplicate()); } size_t getPosition() const override; bool seek(size_t position) override; bool move(long offset) override; std::unique_ptr fork() const { return std::unique_ptr(this->onFork()); } size_t getLength() const override; private: explicit SkFILEStream(FILE*, size_t size, size_t start); explicit SkFILEStream(std::shared_ptr, size_t end, size_t start); explicit SkFILEStream(std::shared_ptr, size_t end, size_t start, size_t current); SkStreamAsset* onDuplicate() const override; SkStreamAsset* onFork() const override; std::shared_ptr fFILE; // My own council will I keep on sizes and offsets. // These are seek positions in the underling FILE, not offsets into the stream. size_t fEnd; size_t fStart; size_t fCurrent; using INHERITED = SkStreamAsset; }; class SK_API SkMemoryStream : public SkStreamMemory { public: SkMemoryStream(); /** We allocate (and free) the memory. Write to it via getMemoryBase() */ SkMemoryStream(size_t length); /** If copyData is true, the stream makes a private copy of the data. */ SkMemoryStream(const void* data, size_t length, bool copyData = false); /** Creates the stream to read from the specified data */ SkMemoryStream(sk_sp data); /** Returns a stream with a copy of the input data. */ static std::unique_ptr MakeCopy(const void* data, size_t length); /** Returns a stream with a bare pointer reference to the input data. */ static std::unique_ptr MakeDirect(const void* data, size_t length); /** Returns a stream with a shared reference to the input data. */ static std::unique_ptr Make(sk_sp data); /** Resets the stream to the specified data and length, just like the constructor. if copyData is true, the stream makes a private copy of the data */ virtual void setMemory(const void* data, size_t length, bool copyData = false); /** Replace any memory buffer with the specified buffer. The caller must have allocated data with sk_malloc or sk_realloc, since it will be freed with sk_free. */ void setMemoryOwned(const void* data, size_t length); sk_sp asData() const { return fData; } void setData(sk_sp data); void skipToAlign4(); const void* getAtPos(); size_t read(void* buffer, size_t size) override; bool isAtEnd() const override; size_t peek(void* buffer, size_t size) const override; bool rewind() override; std::unique_ptr duplicate() const { return std::unique_ptr(this->onDuplicate()); } size_t getPosition() const override; bool seek(size_t position) override; bool move(long offset) override; std::unique_ptr fork() const { return std::unique_ptr(this->onFork()); } size_t getLength() const override; const void* getMemoryBase() override; private: SkMemoryStream* onDuplicate() const override; SkMemoryStream* onFork() const override; sk_sp fData; size_t fOffset; using INHERITED = SkStreamMemory; }; ///////////////////////////////////////////////////////////////////////////////////////////// class SK_API SkFILEWStream : public SkWStream { public: SkFILEWStream(const char path[]); ~SkFILEWStream() override; /** Returns true if the current path could be opened. */ bool isValid() const { return fFILE != nullptr; } bool write(const void* buffer, size_t size) override; void flush() override; void fsync(); size_t bytesWritten() const override; private: FILE* fFILE; using INHERITED = SkWStream; }; class SK_API SkDynamicMemoryWStream : public SkWStream { public: SkDynamicMemoryWStream() = default; SkDynamicMemoryWStream(SkDynamicMemoryWStream&&); SkDynamicMemoryWStream& operator=(SkDynamicMemoryWStream&&); ~SkDynamicMemoryWStream() override; bool write(const void* buffer, size_t size) override; size_t bytesWritten() const override; bool read(void* buffer, size_t offset, size_t size); /** More efficient version of read(dst, 0, bytesWritten()). */ void copyTo(void* dst) const; bool writeToStream(SkWStream* dst) const; /** Equivalent to copyTo() followed by reset(), but may save memory use. */ void copyToAndReset(void* dst); /** Equivalent to writeToStream() followed by reset(), but may save memory use. */ bool writeToAndReset(SkWStream* dst); /** Equivalent to writeToStream() followed by reset(), but may save memory use. When the dst is also a SkDynamicMemoryWStream, the implementation is constant time. */ bool writeToAndReset(SkDynamicMemoryWStream* dst); /** Prepend this stream to dst, resetting this. */ void prependToAndReset(SkDynamicMemoryWStream* dst); /** Return the contents as SkData, and then reset the stream. */ sk_sp detachAsData(); /** Reset, returning a reader stream with the current content. */ std::unique_ptr detachAsStream(); /** Reset the stream to its original, empty, state. */ void reset(); void padToAlign4(); private: struct Block; Block* fHead = nullptr; Block* fTail = nullptr; size_t fBytesWrittenBeforeTail = 0; #ifdef SK_DEBUG void validate() const; #else void validate() const {} #endif // For access to the Block type. friend class SkBlockMemoryStream; friend class SkBlockMemoryRefCnt; using INHERITED = SkWStream; }; #endif