Update to 5.0.0-beta1
[profile/ivi/qtdeclarative.git] / src / qml / qml / qqmlbundle.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtQml module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qqmlbundle_p.h"
43 #include <QtCore/QtCore>
44 #include <iostream>
45 #include <cstdlib>
46
47 static const unsigned char qmlBundleHeaderData[] = { 255, 'q', 'm', 'l', 'd', 'i', 'r', 255 };
48 static const unsigned int qmlBundleHeaderLength = 8;
49
50 //
51 // Entries
52 //
53 QString QQmlBundle::FileEntry::fileName() const
54 {
55     return QString((QChar *)&data[0], fileNameLength / sizeof(QChar));
56 }
57
58 bool QQmlBundle::FileEntry::isFileName(const QString &fileName) const
59 {
60     return fileName.length() * sizeof(QChar) == (unsigned)fileNameLength &&
61            0 == ::memcmp(fileName.constData(), &data[0], fileNameLength);
62 }
63
64 const char *QQmlBundle::FileEntry::contents() const {
65     return &data[fileNameLength];
66 }
67
68 quint32 QQmlBundle::FileEntry::fileSize() const
69 {
70     return size - (sizeof(FileEntry) + fileNameLength);
71 }
72
73
74 //
75 // QQmlBundle
76 //
77 QQmlBundle::QQmlBundle(const QString &fileName)
78 : file(fileName),
79   buffer(0),
80   bufferSize(0),
81   opened(false),
82   headerWritten(false)
83 {
84 }
85
86 QQmlBundle::~QQmlBundle()
87 {
88     close();
89 }
90
91 bool QQmlBundle::open(QIODevice::OpenMode mode)
92 {
93     if (!opened) {
94         if (!file.open(mode))
95             return false;
96
97         bufferSize = file.size();
98         buffer = file.map(0, bufferSize);
99
100         if (bufferSize == 0 ||
101             (bufferSize >= 8 && 0 == ::memcmp(buffer, qmlBundleHeaderData, qmlBundleHeaderLength))) {
102             opened = true;
103             headerWritten = false;
104             return true;
105         } else {
106             close();
107             return false;
108         }
109     }
110     return true;
111 }
112
113 void QQmlBundle::close()
114 {
115     if (opened) {
116         opened = false;
117         headerWritten = false;
118         file.unmap(buffer);
119         file.close();
120     }
121 }
122
123 QList<const QQmlBundle::FileEntry *> QQmlBundle::files() const
124 {
125     QList<const FileEntry *> files;
126     const char *ptr = (const char *) buffer + qmlBundleHeaderLength;
127     const char *end = (const char *) buffer + bufferSize;
128
129     while (ptr < end) {
130         const Entry *cmd = (const Entry *) ptr;
131
132         switch (static_cast<Entry::Kind>(cmd->kind)) {
133         case Entry::File: {
134             const FileEntry *f = reinterpret_cast<const FileEntry *>(cmd);
135             files.append(f);
136         }   break;
137
138         case Entry::Link:
139         case Entry::Skip: {
140             // Skip
141         }   break;
142
143         default:
144             // throw an error
145             return QList<const FileEntry *>();
146         } // switch
147
148         ptr += cmd->size;
149         Q_ASSERT(ptr <= end); // throw an error
150     }
151     return files;
152 }
153
154 void QQmlBundle::remove(const FileEntry *entry)
155 {
156     Q_ASSERT(entry->kind == Entry::File); // ### throw an error
157     Q_ASSERT(file.isWritable());
158     const_cast<FileEntry *>(entry)->kind = Entry::Skip;
159 }
160
161 int QQmlBundle::bundleHeaderLength()
162 {
163     return qmlBundleHeaderLength;
164 }
165
166 bool QQmlBundle::isBundleHeader(const char *data, int size)
167 {
168     if ((unsigned int)size < qmlBundleHeaderLength)
169         return false;
170
171     return 0 == ::memcmp(data, qmlBundleHeaderData, qmlBundleHeaderLength);
172 }
173
174 //
175 // find a some empty space we can use to insert new entries.
176 //
177 const QQmlBundle::Entry *QQmlBundle::findInsertPoint(quint32 size, qint32 *offset)
178 {
179     const char *ptr = (const char *) buffer + qmlBundleHeaderLength;
180     const char *end = (const char *) buffer + bufferSize;
181
182     while (ptr < end) {
183         const Entry *cmd = (const Entry *) ptr;
184
185         if (cmd->kind == Entry::Skip && size + sizeof(RawEntry) < cmd->size) {
186             *offset = ptr - ((const char *) buffer + qmlBundleHeaderLength);
187             return cmd;
188         }
189
190         ptr += cmd->size;
191         Q_ASSERT(ptr <= end); // throw an error
192     }
193
194     return 0;
195 }
196
197 const QQmlBundle::FileEntry *QQmlBundle::find(const QString &fileName) const
198 {
199     const char *ptr = (const char *) buffer + qmlBundleHeaderLength;
200     const char *end = (const char *) buffer + bufferSize;
201
202     while (ptr < end) {
203         const Entry *cmd = (const Entry *) ptr;
204
205         if (cmd->kind == Entry::File) {
206             const FileEntry *fileEntry = static_cast<const FileEntry *>(cmd);
207
208             if (fileEntry->isFileName(fileName))
209                 return fileEntry;
210         }
211
212         ptr += cmd->size;
213         Q_ASSERT(ptr <= end); // throw an error
214     }
215
216     return 0;
217 }
218
219 const QQmlBundle::FileEntry *QQmlBundle::link(const FileEntry *entry, const QString &linkName) const
220 {
221     const char *ptr = (const char *) buffer + entry->link;
222
223     while (ptr != (const char *)buffer) {
224         const Entry *cmd = (const Entry *) ptr;
225         Q_ASSERT(cmd->kind == Entry::Link);
226
227         const FileEntry *fileEntry = static_cast<const FileEntry *>(cmd);
228         if (fileEntry->fileName() == linkName)
229             return fileEntry;
230
231         ptr = (const char *) buffer + fileEntry->link;
232     }
233
234     return 0;
235 }
236
237 const QQmlBundle::FileEntry *QQmlBundle::find(const QChar *fileName, int length) const
238 {
239     return find(QString::fromRawData(fileName, length));
240 }
241
242 bool QQmlBundle::add(const QString &name, const QString &fileName)
243 {
244     if (!file.isWritable())
245         return false;
246     else if (find(fileName))
247         return false;
248
249     QFile inputFile(fileName);
250     if (!inputFile.open(QFile::ReadOnly))
251         return false;
252
253     // ### use best-fit algorithm
254     if (!file.atEnd())
255         file.seek(file.size());
256
257     FileEntry cmd;
258     const quint32 inputFileSize = inputFile.size();
259
260     cmd.kind = Entry::File;
261     cmd.link = 0;
262     cmd.size = sizeof(FileEntry) + name.length() * sizeof(QChar) + inputFileSize;
263     cmd.fileNameLength = name.length() * sizeof(QChar);
264
265     if (bufferSize == 0 && headerWritten == false) {
266         file.write((const char *)qmlBundleHeaderData, qmlBundleHeaderLength);
267         headerWritten = true;
268     }
269
270     file.write((const char *) &cmd, sizeof(FileEntry));
271     file.write((const char *) name.constData(), name.length() * sizeof(QChar));
272
273     uchar *source = inputFile.map(0, inputFileSize);
274     file.write((const char *) source, inputFileSize);
275     inputFile.unmap(source);
276     return true;
277 }
278
279 bool QQmlBundle::add(const QString &fileName)
280 {
281     return add(fileName, fileName);
282 }
283
284 bool QQmlBundle::addMetaLink(const QString &fileName,
285                              const QString &linkName,
286                              const QByteArray &data)
287 {
288     if (!file.isWritable())
289         return false;
290
291     const FileEntry *fileEntry = find(fileName);
292     if (!fileEntry)
293         return false;
294
295     // ### use best-fit algorithm
296     if (!file.atEnd())
297         file.seek(file.size());
298
299     FileEntry cmd;
300
301     const quint32 inputFileSize = data.size();
302
303     cmd.kind = Entry::Link;
304     cmd.link = fileEntry->link;
305     cmd.size = sizeof(FileEntry) + linkName.length() * sizeof(QChar) + inputFileSize;
306     cmd.fileNameLength = linkName.length() * sizeof(QChar);
307
308     if (bufferSize == 0 && headerWritten == false) {
309         file.write((const char *)qmlBundleHeaderData, qmlBundleHeaderLength);
310         headerWritten = true;
311     }
312
313     const_cast<FileEntry *>(fileEntry)->link = file.size();
314
315     file.write((const char *) &cmd, sizeof(FileEntry));
316     file.write((const char *) linkName.constData(), linkName.length() * sizeof(QChar));
317     file.write((const char *) data.constData(), inputFileSize);
318     return true;
319 }