Add file stream API.
[platform/core/uifw/dali-adaptor.git] / dali / internal / adaptor-framework / generic / file-stream-impl-generic.cpp
1 /*
2  * Copyright (c) 2019 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 // CLASS HEADER
18 #include <dali/internal/adaptor-framework/common/file-stream-impl.h>
19
20 // EXTERNAL INCLUDES
21 #include <string>
22 #include <fstream>
23
24 #include <dali/integration-api/debug.h>
25
26 namespace Dali
27 {
28
29 FileStream::Impl::Impl(const std::string& filename, uint8_t mode)
30 : mFileName( filename ),
31   mMode( mode ),
32   mBuffer( nullptr ),
33   mDataSize( 0 ),
34   mFile( nullptr )
35 {
36   DALI_ASSERT_DEBUG( !filename.empty() && "Can't open a empty filename." );
37   DALI_ASSERT_DEBUG( mode != 0 && "No mode is undefined behaviour" );
38 }
39
40 FileStream::Impl::Impl(uint8_t* buffer, size_t dataSize, uint8_t mode)
41 : mMode( mode ),
42   mBuffer( buffer ),
43   mDataSize( dataSize ),
44   mFile( nullptr )
45 {
46   DALI_ASSERT_DEBUG( buffer != 0 && "Can't open file on null buffer." );
47   DALI_ASSERT_DEBUG( dataSize > 0 && "Pointless to open file on empty buffer." );
48   DALI_ASSERT_DEBUG( mode != 0 && "No mode is undefined behaviour." );
49 }
50
51 FileStream::Impl::Impl(Dali::Vector<uint8_t>& vector, size_t dataSize, uint8_t mode)
52 : mMode( mode ),
53   mBuffer( nullptr ),
54   mDataSize( dataSize ),
55   mFile( nullptr )
56 {
57   // Resize the buffer to ensure any null that gets written by
58   // fmemopen is written past the end of any data that is written to the buffer.
59   // (Workaround for a bug in Ubuntu that overwrites null to the last byte of the
60   // data block regardless of whether binary mode was specified. Tizen doesn't write
61   // null if binary mode is specified).
62
63   ++mDataSize;
64   vector.Resize( mDataSize );
65   mBuffer = &vector[0];
66
67   DALI_ASSERT_DEBUG( mBuffer != nullptr && "Can't open file on null buffer." );
68   DALI_ASSERT_DEBUG( dataSize > 0 && "Pointless to open file on empty buffer." );
69   DALI_ASSERT_DEBUG( mode != 0 && "No mode is undefined behaviour." );
70 }
71
72 FileStream::Impl::~Impl()
73 {
74   if( mFile )
75   {
76     const int closeFailed = fclose( mFile );
77     if( closeFailed )
78     {
79       DALI_LOG_WARNING( "File close failed for FILE: \"%p\".\n", static_cast<void*>( mFile ) );
80     }
81
82     mFile = nullptr;
83   }
84
85   if( mFileStream.is_open() )
86   {
87     mFileStream.close();
88   }
89 }
90
91 std::iostream& FileStream::Impl::GetStream()
92 {
93   if( mFile )
94   {
95     // return empty stream if FILE stream is open to avoid simultaneous access to the same file
96     return mFileStream;
97   }
98
99   if( mFileStream.is_open() )
100   {
101     return mFileStream;
102   }
103
104   if( mBufferStream.rdbuf()->in_avail() )
105   {
106     return mBufferStream;
107   }
108
109   std::ios_base::openmode openMode = std::ios::ate;
110   if( mMode & Dali::FileStream::BINARY )
111   {
112     openMode |= std::ios::binary;
113   }
114
115   if( mMode & Dali::FileStream::WRITE )
116   {
117     openMode |= std::ios::out;
118   }
119   else
120   {
121     openMode |= std::ios::in;
122   }
123
124   if( !mFileName.empty() )
125   {
126     mFileStream.open( mFileName, openMode );
127     if( !mFileStream.is_open() )
128     {
129       DALI_LOG_WARNING( "stream open failed for: \"%s\", in mode: \"%d\".\n", mFileName, static_cast<int>( openMode ) );
130     }
131     return mFileStream;
132   }
133   else if( mBuffer )
134   {
135     mBufferStream.rdbuf()->pubsetbuf( reinterpret_cast<char*>( mBuffer ), mDataSize );
136     if( !mBufferStream.rdbuf()->in_avail() )
137     {
138       DALI_LOG_WARNING( "File open failed for memory buffer at location: \"%p\", of size: \"%u\", in mode: \"%d\".\n",
139           static_cast<void*>( mBuffer ), static_cast<unsigned>( mDataSize ), static_cast<int>( openMode ) );
140     }
141   }
142
143   return mBufferStream;
144 }
145
146 FILE* FileStream::Impl::GetFile()
147 {
148   if( mFileStream.is_open() || mBufferStream.rdbuf()->in_avail() )
149   {
150     // return empty FILE stream if the stream is open to avoid simultaneous access to the same file
151     return nullptr;
152   }
153
154   if( mFile )
155   {
156     return mFile;
157   }
158
159   char openMode[16] = { 0 };
160   int i = 0;
161
162   if( mMode & Dali::FileStream::WRITE )
163   {
164     openMode[i++] = 'w';
165   }
166   else
167   {
168     openMode[i++] = 'r';
169   }
170
171   if( mMode & Dali::FileStream::BINARY )
172   {
173     openMode[i++] = 'b';
174   }
175
176   openMode[i++] = 0;
177
178   if( !mFileName.empty() )
179   {
180     mFile = fopen( mFileName.c_str(), openMode );
181     if( !mFile )
182     {
183       DALI_LOG_WARNING( "file open failed for: \"%s\", in mode: \"%s\".\n", mFileName, openMode );
184     }
185   }
186   else if( mBuffer )
187   {
188     mFile = fmemopen( mBuffer, mDataSize, openMode );
189     if( !mFile )
190     {
191       DALI_LOG_WARNING( "File open failed for memory buffer at location: \"%p\", of size: \"%u\", in mode: \"%s\".\n",
192           static_cast<void*>( mBuffer ), static_cast<unsigned>( mDataSize ), openMode );
193     }
194   }
195
196   return mFile;
197 }
198
199 } // Dali