1 package org.robolectric.res.android;
2 
3 import static org.robolectric.res.android.Asset.toIntExact;
4 import static org.robolectric.res.android.Util.ALOGV;
5 
6 import java.io.FileInputStream;
7 import java.io.IOException;
8 import java.io.InputStream;
9 import java.util.Enumeration;
10 import java.util.zip.ZipEntry;
11 import java.util.zip.ZipFile;
12 
13 public class FileMap {
14 
15   private ZipFile zipFile;
16   private ZipEntry zipEntry;
17   private boolean readOnly;
18   private int fd;
19   private boolean isFromZip;
20 
21   // Create a new mapping on an open file.
22 //
23 // Closing the file descriptor does not unmap the pages, so we don't
24 // claim ownership of the fd.
25 //
26 // Returns "false" on failure.
create(String origFileName, int fd, long offset, int length, boolean readOnly)27   boolean create(String origFileName, int fd, long offset, int length,
28       boolean readOnly)
29   {
30     this.mFileName = origFileName;
31     this.fd = fd;
32     this.mDataOffset = offset;
33     this.readOnly = readOnly;
34     return true;
35   }
36 // #if defined(__MINGW32__)
37 //     int     adjust;
38 //     off64_t adjOffset;
39 //     size_t  adjLength;
40 //
41 //     if (mPageSize == -1) {
42 //       SYSTEM_INFO  si;
43 //
44 //       GetSystemInfo( &si );
45 //       mPageSize = si.dwAllocationGranularity;
46 //     }
47 //
48 //     DWORD  protect = readOnly ? PAGE_READONLY : PAGE_READWRITE;
49 //
50 //     mFileHandle  = (HANDLE) _get_osfhandle(fd);
51 //     mFileMapping = CreateFileMapping( mFileHandle, NULL, protect, 0, 0, NULL);
52 //     if (mFileMapping == NULL) {
53 //       ALOGE("CreateFileMapping(%s, %" PRIx32 ") failed with error %" PRId32 "\n",
54 //           mFileHandle, protect, GetLastError() );
55 //       return false;
56 //     }
57 //
58 //     adjust    = offset % mPageSize;
59 //     adjOffset = offset - adjust;
60 //     adjLength = length + adjust;
61 //
62 //     mBasePtr = MapViewOfFile( mFileMapping,
63 //         readOnly ? FILE_MAP_READ : FILE_MAP_ALL_ACCESS,
64 //         0,
65 //         (DWORD)(adjOffset),
66 //         adjLength );
67 //     if (mBasePtr == NULL) {
68 //       ALOGE("MapViewOfFile(%" PRId64 ", 0x%x) failed with error %" PRId32 "\n",
69 //           adjOffset, adjLength, GetLastError() );
70 //       CloseHandle(mFileMapping);
71 //       mFileMapping = INVALID_HANDLE_VALUE;
72 //       return false;
73 //     }
74 // #else // !defined(__MINGW32__)
75 //     int     prot, flags, adjust;
76 //     off64_t adjOffset;
77 //     size_t  adjLength;
78 //
79 //     void* ptr;
80 //
81 //     assert(fd >= 0);
82 //     assert(offset >= 0);
83 //     assert(length > 0);
84 //
85 //     // init on first use
86 //     if (mPageSize == -1) {
87 //       mPageSize = sysconf(_SC_PAGESIZE);
88 //       if (mPageSize == -1) {
89 //         ALOGE("could not get _SC_PAGESIZE\n");
90 //         return false;
91 //       }
92 //     }
93 //
94 //     adjust = offset % mPageSize;
95 //     adjOffset = offset - adjust;
96 //     adjLength = length + adjust;
97 //
98 //     flags = MAP_SHARED;
99 //     prot = PROT_READ;
100 //     if (!readOnly)
101 //       prot |= PROT_WRITE;
102 //
103 //     ptr = mmap(NULL, adjLength, prot, flags, fd, adjOffset);
104 //     if (ptr == MAP_FAILED) {
105 //       ALOGE("mmap(%lld,0x%x) failed: %s\n",
106 //           (long long)adjOffset, adjLength, strerror(errno));
107 //       return false;
108 //     }
109 //     mBasePtr = ptr;
110 // #endif // !defined(__MINGW32__)
111 //
112 //       mFileName = origFileName != NULL ? strdup(origFileName) : NULL;
113 //     mBaseLength = adjLength;
114 //     mDataOffset = offset;
115 //     mDataPtr = (char*) mBasePtr + adjust;
116 //     mDataLength = length;
117 //
118 //     assert(mBasePtr != NULL);
119 //
120 //     ALOGV("MAP: base %s/0x%x data %s/0x%x\n",
121 //         mBasePtr, mBaseLength, mDataPtr, mDataLength);
122 //
123 //     return true;
124 //   }
125 
126 
createFromZip(String origFileName, ZipFile zipFile, ZipEntry entry, int length, boolean readOnly)127   public boolean createFromZip(String origFileName, ZipFile zipFile, ZipEntry entry, int length,
128       boolean readOnly) {
129     isFromZip = true;
130     this.zipFile = zipFile;
131     this.zipEntry = entry;
132 
133     int     prot, flags, adjust;
134     long adjOffset;
135     int  adjLength;
136 
137     int ptr;
138     long offset = guessOffsetFor(zipFile, entry);
139 
140     assert(fd >= 0);
141     assert(offset >= 0);
142     // assert(length > 0);
143 
144     // init on first use
145 //    if (mPageSize == -1) {
146 //      mPageSize = sysconf(_SC_PAGESIZE);
147 //      if (mPageSize == -1) {
148 //        ALOGE("could not get _SC_PAGESIZE\n");
149 //        return false;
150 //      }
151 //    }
152 
153     // adjust = Math.toIntExact(offset % mPageSize);
154     // adjOffset = offset - adjust;
155     // adjLength = length + adjust;
156 
157     //flags = MAP_SHARED;
158     //prot = PROT_READ;
159     //if (!readOnly)
160     //  prot |= PROT_WRITE;
161 
162     // ptr = mmap(null, adjLength, prot, flags, fd, adjOffset);
163     // if (ptr == MAP_FAILED) {
164     //   ALOGE("mmap(%lld,0x%x) failed: %s\n",
165     //       (long long)adjOffset, adjLength, strerror(errno));
166     //   return false;
167     // }
168     // mBasePtr = ptr;
169 
170     mFileName = origFileName != null ? origFileName : null;
171     //mBaseLength = adjLength;
172     mDataOffset = offset;
173     //mDataPtr = mBasePtr + adjust;
174     mDataLength = toIntExact(entry.getSize());
175 
176     //assert(mBasePtr != 0);
177 
178     ALOGV("MAP: base %s/0x%x data %s/0x%x\n",
179         mBasePtr, mBaseLength, mDataPtr, mDataLength);
180 
181     return true;
182   }
183 
guessOffsetFor(ZipFile zipFile, ZipEntry zipEntry)184   long guessOffsetFor(ZipFile zipFile, ZipEntry zipEntry) {
185     Enumeration<? extends ZipEntry> zipEntries = zipFile.entries();
186     long offset = 0;
187     while (zipEntries.hasMoreElements())
188     {
189       ZipEntry entry = zipEntries.nextElement();
190       long fileSize = 0;
191       long extra = entry.getExtra() == null ? 0 : entry.getExtra().length;
192       offset += 30 + entry.getName().length() + extra;
193 
194       if (entry.getName().equals(zipEntry.getName())) {
195         return offset;
196       }
197 
198       if(!entry.isDirectory())
199       {
200         fileSize = entry.getCompressedSize();
201 
202         // Do stuff here with fileSize & offset
203       }
204       offset += fileSize;
205     }
206     throw new IllegalStateException("'" + zipEntry.getName() + "' not found");
207   }
208   /*
209    * This represents a memory-mapped file.  It might be the entire file or
210    * only part of it.  This requires a little bookkeeping because the mapping
211    * needs to be aligned on page boundaries, and in some cases we'd like to
212    * have multiple references to the mapped area without creating additional
213    * maps.
214    *
215    * This always uses MAP_SHARED.
216    *
217    * TODO: we should be able to create a new FileMap that is a subset of
218    * an existing FileMap and shares the underlying mapped pages.  Requires
219    * completing the refcounting stuff and possibly introducing the notion
220    * of a FileMap hierarchy.
221    */
222   // class FileMap {
223   //   public:
224   //   FileMap(void);
225   //
226   //   FileMap(FileMap&& f);
227   //   FileMap& operator=(FileMap&& f);
228 
229   /*
230    * Create a new mapping on an open file.
231    *
232    * Closing the file descriptor does not unmap the pages, so we don't
233    * claim ownership of the fd.
234    *
235    * Returns "false" on failure.
236    */
237   // boolean create(String origFileName, int fd,
238   //     long offset, int length, boolean readOnly) {
239   // }
240 
241     // ~FileMap(void);
242 
243     /*
244      * Return the name of the file this map came from, if known.
245      */
getFileName()246     String getFileName() { return mFileName; }
247 
248     /*
249      * Get a pointer to the piece of the file we requested.
250      */
getDataPtr()251   synchronized byte[] getDataPtr() {
252     if (mDataPtr == null) {
253       mDataPtr = new byte[mDataLength];
254 
255       InputStream is;
256       try {
257         if (isFromZip) {
258           is = zipFile.getInputStream(zipEntry);
259         } else {
260           is = new FileInputStream(getFileName());
261         }
262         try {
263           readFully(is, mDataPtr);
264         } finally {
265           is.close();
266         }
267       } catch (IOException e) {
268         throw new RuntimeException(e);
269       }
270     }
271     return mDataPtr;
272   }
273 
readFully(InputStream is, byte[] bytes)274   public static void readFully(InputStream is, byte[] bytes) throws IOException {
275     int size = bytes.length;
276     int remaining = size;
277     while (remaining > 0) {
278       int location = size - remaining;
279       int bytesRead = is.read(bytes, location, remaining);
280       if (bytesRead == -1) {
281         break;
282       }
283       remaining -= bytesRead;
284     }
285 
286     if (remaining > 0) {
287       throw new RuntimeException("failed to read " + size + " (" + remaining + " bytes unread)");
288     }
289   }
290 
291   /*
292    * Get the length we requested.
293    */
getDataLength()294   int getDataLength() { return mDataLength; }
295 
296   /*
297    * Get the data offset used to create this map.
298    */
getDataOffset()299   long getDataOffset() { return mDataOffset; }
300 
getZipEntry()301   public ZipEntry getZipEntry() {
302     return zipEntry;
303   }
304 
305   //   /*
306 //    * This maps directly to madvise() values, but allows us to avoid
307 //    * including <sys/mman.h> everywhere.
308 //    */
309 //   enum MapAdvice {
310 //     NORMAL, RANDOM, SEQUENTIAL, WILLNEED, DONTNEED
311 //   };
312 //
313 //   /*
314 //    * Apply an madvise() call to the entire file.
315 //    *
316 //    * Returns 0 on success, -1 on failure.
317 //    */
318 //   int advise(MapAdvice advice);
319 //
320 //   protected:
321 //
322 //   private:
323 //   // these are not implemented
324 //   FileMap(const FileMap& src);
325 //     const FileMap& operator=(const FileMap& src);
326 //
327   String       mFileName;      // original file name, if known
328   int       mBasePtr;       // base of mmap area; page aligned
329   int      mBaseLength;    // length, measured from "mBasePtr"
330   long     mDataOffset;    // offset used when map was created
331   byte[]       mDataPtr;       // start of requested data, offset from base
332   int      mDataLength;    // length, measured from "mDataPtr"
333   static long mPageSize;
334 
335   @Override
toString()336   public String toString() {
337     if (isFromZip) {
338       return "FileMap{" +
339           "zipFile=" + zipFile.getName() +
340           ", zipEntry=" + zipEntry +
341           '}';
342     } else {
343       return "FileMap{" +
344           "mFileName='" + mFileName + '\'' +
345           '}';
346     }
347   }
348 }
349