Stream.cpp 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. /**
  2. * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
  3. * SPDX-License-Identifier: Apache-2.0.
  4. */
  5. #include <aws/crt/StlAllocator.h>
  6. #include <aws/crt/io/Stream.h>
  7. #include <aws/io/stream.h>
  8. namespace Aws
  9. {
  10. namespace Crt
  11. {
  12. namespace Io
  13. {
  14. InputStream::~InputStream()
  15. {
  16. // DO NOTHING: for now. But keep this here because it has to be virtual, and we may have
  17. // resources to clean up in the future.
  18. }
  19. int InputStream::s_Seek(aws_input_stream *stream, int64_t offset, enum aws_stream_seek_basis basis)
  20. {
  21. auto impl = static_cast<InputStream *>(stream->impl);
  22. // Detect whether implementation raises an error when reporting failure.
  23. // Docs for C++ SeekImpl API say you "SHOULD" raise an error,
  24. // but the C API does in fact require an error to be raised.
  25. aws_reset_error();
  26. if (impl->SeekImpl(offset, static_cast<StreamSeekBasis>(basis)))
  27. {
  28. return AWS_OP_SUCCESS;
  29. }
  30. if (aws_last_error() == 0)
  31. {
  32. aws_raise_error(AWS_IO_STREAM_SEEK_FAILED);
  33. }
  34. return AWS_OP_ERR;
  35. }
  36. int InputStream::s_Read(aws_input_stream *stream, aws_byte_buf *dest)
  37. {
  38. auto impl = static_cast<InputStream *>(stream->impl);
  39. // Detect whether implementation raises an error when reporting failure.
  40. // Docs for C++ ReadImpl API say you "SHOULD" raise an error,
  41. // but the C API does in fact require an error to be raised.
  42. aws_reset_error();
  43. if (impl->ReadImpl(*dest))
  44. {
  45. return AWS_OP_SUCCESS;
  46. }
  47. if (aws_last_error() == 0)
  48. {
  49. aws_raise_error(AWS_IO_STREAM_READ_FAILED);
  50. }
  51. return AWS_OP_ERR;
  52. }
  53. int InputStream::s_GetStatus(aws_input_stream *stream, aws_stream_status *status)
  54. {
  55. auto impl = static_cast<InputStream *>(stream->impl);
  56. *status = impl->GetStatusImpl();
  57. return AWS_OP_SUCCESS;
  58. }
  59. int InputStream::s_GetLength(struct aws_input_stream *stream, int64_t *out_length)
  60. {
  61. auto impl = static_cast<InputStream *>(stream->impl);
  62. int64_t length = impl->GetLengthImpl();
  63. if (length >= 0)
  64. {
  65. *out_length = length;
  66. return AWS_OP_SUCCESS;
  67. }
  68. aws_raise_error(AWS_IO_STREAM_READ_FAILED);
  69. return AWS_OP_ERR;
  70. }
  71. void InputStream::s_Acquire(aws_input_stream *stream)
  72. {
  73. auto impl = static_cast<InputStream *>(stream->impl);
  74. impl->AcquireRef();
  75. }
  76. void InputStream::s_Release(aws_input_stream *stream)
  77. {
  78. auto impl = static_cast<InputStream *>(stream->impl);
  79. impl->ReleaseRef();
  80. }
  81. aws_input_stream_vtable InputStream::s_vtable = {
  82. InputStream::s_Seek,
  83. InputStream::s_Read,
  84. InputStream::s_GetStatus,
  85. InputStream::s_GetLength,
  86. InputStream::s_Acquire,
  87. InputStream::s_Release,
  88. };
  89. InputStream::InputStream(Aws::Crt::Allocator *allocator)
  90. {
  91. m_allocator = allocator;
  92. AWS_ZERO_STRUCT(m_underlying_stream);
  93. m_underlying_stream.impl = this;
  94. m_underlying_stream.vtable = &s_vtable;
  95. }
  96. StdIOStreamInputStream::StdIOStreamInputStream(
  97. std::shared_ptr<Aws::Crt::Io::IStream> stream,
  98. Aws::Crt::Allocator *allocator) noexcept
  99. : InputStream(allocator), m_stream(std::move(stream))
  100. {
  101. }
  102. bool StdIOStreamInputStream::IsValid() const noexcept
  103. {
  104. auto status = GetStatusImpl();
  105. return status.is_valid;
  106. }
  107. bool StdIOStreamInputStream::ReadImpl(ByteBuf &buffer) noexcept
  108. {
  109. // so this blocks, but readsome() doesn't work at all, so this is the best we've got.
  110. // if you don't like this, don't use std::input_stream and implement your own version
  111. // of Aws::Crt::Io::InputStream.
  112. m_stream->read(reinterpret_cast<char *>(buffer.buffer + buffer.len), buffer.capacity - buffer.len);
  113. auto read = m_stream->gcount();
  114. buffer.len += static_cast<size_t>(read);
  115. if (read > 0 || (read == 0 && m_stream->eof()))
  116. {
  117. return true;
  118. }
  119. auto status = GetStatusImpl();
  120. return status.is_valid && !status.is_end_of_stream;
  121. }
  122. StreamStatus StdIOStreamInputStream::GetStatusImpl() const noexcept
  123. {
  124. StreamStatus status;
  125. status.is_end_of_stream = m_stream->eof();
  126. status.is_valid = static_cast<bool>(*m_stream);
  127. return status;
  128. }
  129. int64_t StdIOStreamInputStream::GetLengthImpl() const noexcept
  130. {
  131. auto currentPosition = m_stream->tellg();
  132. m_stream->seekg(0, std::ios_base::end);
  133. int64_t retVal = -1;
  134. if (*m_stream)
  135. {
  136. retVal = static_cast<int64_t>(m_stream->tellg());
  137. }
  138. m_stream->seekg(currentPosition);
  139. return retVal;
  140. }
  141. bool StdIOStreamInputStream::SeekImpl(int64_t offset, StreamSeekBasis seekBasis) noexcept
  142. {
  143. // very important, otherwise the stream can't be reused after reading the entire stream the first time.
  144. m_stream->clear();
  145. auto seekDir = std::ios_base::beg;
  146. switch (seekBasis)
  147. {
  148. case StreamSeekBasis::Begin:
  149. seekDir = std::ios_base::beg;
  150. break;
  151. case StreamSeekBasis::End:
  152. seekDir = std::ios_base::end;
  153. break;
  154. default:
  155. aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
  156. return false;
  157. }
  158. using stdOffType = Aws::Crt::Io::IStream::off_type;
  159. if (offset < std::numeric_limits<stdOffType>::min() || offset > std::numeric_limits<stdOffType>::max())
  160. {
  161. aws_raise_error(AWS_IO_STREAM_INVALID_SEEK_POSITION);
  162. return false;
  163. }
  164. m_stream->seekg(static_cast<stdOffType>(offset), seekDir);
  165. return true;
  166. }
  167. } // namespace Io
  168. } // namespace Crt
  169. } // namespace Aws