[dali_2.3.29] Merge branch 'devel/master'
[platform/core/uifw/dali-adaptor.git] / dali / internal / system / common / file-closer.h
1 #ifndef DALI_INTERNAL_PLATFORM_FILECLOSER_H
2 #define DALI_INTERNAL_PLATFORM_FILECLOSER_H
3 /*
4  * Copyright (c) 2021 Samsung Electronics Co., Ltd.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  */
19
20 // INTERNAL INCLUDES
21 #include <dali/devel-api/adaptor-framework/file-loader.h>
22
23 // EXTERNAL INCLUDES
24 #include <cstdio>
25
26 namespace Dali
27 {
28 namespace Internal
29 {
30 namespace Platform
31 {
32 /**
33  * Opens files and closes them later even if an exception is thrown.
34  */
35 class FileCloser
36 {
37 protected: // prevent this class being directly instantiated
38   /**
39    * @brief Construct a FileCloser guarding a new FILE* for accessing the path passed in.
40    */
41   FileCloser(const char* const filename, const char* const mode)
42   {
43     DALI_ASSERT_DEBUG(filename != 0 && "Can't open a null filename.");
44     DALI_ASSERT_DEBUG(mode != 0 && "Null mode is undefined behaviour in spec.");
45
46     Dali::FileLoader::FileType fileType = Dali::FileLoader::FileType::TEXT;
47
48     const char* modeStr = mode;
49     while(*modeStr)
50     {
51       switch(*modeStr)
52       {
53         case 'r':
54           break;
55         case 'b':
56           fileType = FileLoader::FileType::BINARY;
57           break;
58         // Still has to use fopen for append and write modes
59         case 'a':
60         case 'w':
61         case '+':
62           mFile = fopen(filename, mode);
63           return;
64         default:
65           break;
66       }
67
68       ++modeStr;
69     }
70
71     std::streampos bufferSize = 0;
72     if(!Dali::FileLoader::ReadFile(filename, bufferSize, mFileBuffer, fileType))
73     {
74       mFile = nullptr;
75     }
76     else
77     {
78       mFile = fmemopen(&mFileBuffer[0], bufferSize, mode);
79     }
80   }
81
82   /**
83    * @brief Construct a FileCloser guarding a FILE* for reading out of the memory buffer passed in.
84    */
85   FileCloser(uint8_t* buffer, size_t dataSize, const char* const mode)
86   : mFile(fmemopen(buffer, dataSize, mode))
87   {
88   }
89
90   FileCloser(Dali::Vector<uint8_t>& vector, size_t dataSize, const char* const mode)
91   {
92     // Resize the buffer to ensure any null that gets written by
93     // fmemopen is written past the end of any data that is written to the buffer.
94     // (Workaround for a bug in Ubuntu that overwrites null to the last byte of the
95     // data block regardless of whether binary mode was specified. Tizen doesn't write
96     // null if binary mode is specified).
97     size_t bufferSize = dataSize;
98     ++bufferSize;
99     vector.Resize(bufferSize);
100
101     void* const buffer = &vector[0];
102     mFile              = fmemopen(buffer, bufferSize, mode);
103
104     DALI_ASSERT_DEBUG(buffer != 0 && "Cant open file on null buffer.");
105     DALI_ASSERT_DEBUG(dataSize > 0 && "Pointless to open file on empty buffer.");
106     DALI_ASSERT_DEBUG(mode != 0 && "Null mode is undefined behaviour in spec.");
107
108     if(mFile == 0)
109     {
110       DALI_LOG_WARNING("File open failed for memory buffer at location: \"%p\", of size: \"%u\", in mode: \"%s\".\n", static_cast<void*>(buffer), static_cast<unsigned>(dataSize), mode);
111     }
112   }
113
114   /**
115     * @brief Destroy the FileCloser and clean up its FILE*.
116     */
117   ~FileCloser()
118   {
119     if(mFile != 0)
120     {
121       const int closeFailed = fclose(mFile);
122
123       if(closeFailed)
124       {
125         DALI_LOG_WARNING("File close failed for FILE: \"%p\".\n", static_cast<void*>(mFile));
126       }
127       mFile = 0;
128     }
129   }
130
131 public:
132   /**
133    * @return The FILE* guarded by this object.
134    */
135   FILE* GetFile()
136   {
137     return mFile;
138   }
139
140 private:
141   // Undefined
142   FileCloser(const FileCloser& fileCloser);
143
144   // Undefined
145   FileCloser& operator=(const FileCloser& fileCloser);
146
147 private:
148   FILE*              mFile;
149   Dali::Vector<char> mFileBuffer;
150 };
151
152 } // namespace Platform
153
154 } // namespace Internal
155
156 } // namespace Dali
157
158 #endif // DALI_INTERNAL_PLATFORM_FILECLOSER_H