Add SkData::NewFromFD.

Chromium needs a SkStream backed by a file descriptor.
Skia already has the code and can do the work, this change exposes the
functionality in Skia in a clean way.

https://codereview.chromium.org/15941025/


git-svn-id: http://skia.googlecode.com/svn/trunk@9408 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/include/core/SkData.h b/include/core/SkData.h
index 0e8ee22..4600429 100644
--- a/include/core/SkData.h
+++ b/include/core/SkData.h
@@ -93,12 +93,22 @@
     /**
      *  Create a new dataref from a SkFILE.
      *  This does not take ownership of the SkFILE, nor close it.
+     *  The caller is free to close the SkFILE at its convenience.
      *  The SkFILE must be open for reading only.
      *  Returns NULL on failure.
      */
     static SkData* NewFromFILE(SkFILE* f);
 
     /**
+     *  Create a new dataref from a file descriptor.
+     *  This does not take ownership of the file descriptor, nor close it.
+     *  The caller is free to close the file descriptor at its convenience.
+     *  The file descriptor must be open for reading only.
+     *  Returns NULL on failure.
+     */
+    static SkData* NewFromFD(int fd);
+    
+    /**
      *  Create a new dataref using a subset of the data in the specified
      *  src dataref.
      */
diff --git a/include/core/SkOSFile.h b/include/core/SkOSFile.h
index 11330a0..f8ce06b 100644
--- a/include/core/SkOSFile.h
+++ b/include/core/SkOSFile.h
@@ -54,10 +54,17 @@
 
 /** Maps a file into memory. Returns the address and length on success, NULL otherwise.
  *  The mapping is read only.
+ *  When finished with the mapping, free the returned pointer with sk_fmunmap.
  */
 void*   sk_fmmap(SkFILE* f, size_t* length);
 
-/** Unmaps a file previously mapped by sk_fmmap.
+/** Maps a file descriptor into memory. Returns the address and length on success, NULL otherwise.
+ *  The mapping is read only.
+ *  When finished with the mapping, free the returned pointer with sk_fmunmap.
+ */
+void*   sk_fdmmap(int fd, size_t* length);
+
+/** Unmaps a file previously mapped by sk_fmmap or sk_fdmmap.
  *  The length parameter must be the same as returned from sk_fmmap.
  */
 void    sk_fmunmap(const void* addr, size_t length);
@@ -65,6 +72,11 @@
 /** Returns true if the two point at the exact same filesystem object. */
 bool    sk_fidentical(SkFILE* a, SkFILE* b);
 
+/** Returns the underlying file descriptor for the given file.
+ *  The return value will be < 0 on failure.
+ */
+int     sk_fileno(SkFILE* f);
+
 // Returns true if something (file, directory, ???) exists at this path.
 bool    sk_exists(const char *path);
 
diff --git a/include/core/SkTemplates.h b/include/core/SkTemplates.h
index ce76dbf..bbbed48 100644
--- a/include/core/SkTemplates.h
+++ b/include/core/SkTemplates.h
@@ -64,6 +64,11 @@
     );
 }
 
+/** Returns true if the source value 's' will fit in the destination type 'D'. */
+template <typename D, typename S> inline bool SkTFitsIn(S s) {
+    return static_cast<D>(s) == s;
+}
+
 /** \class SkAutoTCallVProc
 
     Call a function when this goes out of scope. The template uses two
diff --git a/src/core/SkData.cpp b/src/core/SkData.cpp
index 32e0297..c1a2136 100644
--- a/src/core/SkData.cpp
+++ b/src/core/SkData.cpp
@@ -97,6 +97,16 @@
     return SkData::NewWithProc(addr, size, sk_mmap_releaseproc, NULL);
 }
 
+SkData* SkData::NewFromFD(int fd) {
+    size_t size;
+    void* addr = sk_fdmmap(fd, &size);
+    if (NULL == addr) {
+        return NULL;
+    }
+
+    return SkData::NewWithProc(addr, size, sk_mmap_releaseproc, NULL);
+}
+
 // assumes context is a SkData
 static void sk_dataref_releaseproc(const void*, size_t, void* context) {
     SkData* src = reinterpret_cast<SkData*>(context);
diff --git a/src/ports/SkDebug_brew.cpp b/src/ports/SkDebug_brew.cpp
deleted file mode 100644
index b7ad3ef..0000000
--- a/src/ports/SkDebug_brew.cpp
+++ /dev/null
@@ -1,28 +0,0 @@
-/* libs/corecg/SkDebug_brew.cpp
- *
- * Copyright 2009, The Android Open Source Project
- * Copyright 2009, Company 100, Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkTypes.h"
-
-#ifdef SK_BUILD_FOR_BREW
-
-static const size_t kBufferSize = 256;
-
-#include <AEEStdLib.h>
-#include <stdarg.h>
-
-void SkDebugf(const char format[], ...) {
-    char    buffer[kBufferSize + 1];
-    va_list args;
-    va_start(args, format);
-    VSNPRINTF(buffer, kBufferSize, format, args);
-    va_end(args);
-    DBGPRINTF(buffer);
-}
-
-#endif SK_BUILD_FOR_BREW
diff --git a/src/ports/SkMemory_brew.cpp b/src/ports/SkMemory_brew.cpp
deleted file mode 100644
index 96af702..0000000
--- a/src/ports/SkMemory_brew.cpp
+++ /dev/null
@@ -1,55 +0,0 @@
-/* libs/graphics/ports/SkMemory_brew.cpp
- *
- * Copyright 2009, The Android Open Source Project
- * Copyright 2009, Company 100, Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkTypes.h"
-
-#ifdef SK_BUILD_FOR_BREW
-
-#include <AEEStdLib.h>
-
-void sk_throw() {
-    SkDEBUGFAIL("sk_throw");
-    abort();
-}
-
-void sk_out_of_memory(void) {
-    SkDEBUGFAIL("sk_out_of_memory");
-    abort();
-}
-
-void* sk_malloc_throw(size_t size) {
-    return sk_malloc_flags(size, SK_MALLOC_THROW);
-}
-
-void* sk_realloc_throw(void* addr, size_t size) {
-    void* p = REALLOC(addr, size | ALLOC_NO_ZMEM);
-    if (size == 0) {
-        return p;
-    }
-    if (p == NULL) {
-        sk_throw();
-    }
-    return p;
-}
-
-void sk_free(void* p) {
-    FREEIF(p);
-}
-
-void* sk_malloc_flags(size_t size, unsigned flags) {
-    void* p = MALLOC(size | ALLOC_NO_ZMEM);
-    if (p == NULL) {
-        if (flags & SK_MALLOC_THROW) {
-            sk_throw();
-        }
-    }
-    return p;
-}
-
-#endif
diff --git a/src/ports/SkOSFile_brew.cpp b/src/ports/SkOSFile_brew.cpp
deleted file mode 100644
index 50e133f..0000000
--- a/src/ports/SkOSFile_brew.cpp
+++ /dev/null
@@ -1,90 +0,0 @@
-/* libs/graphics/ports/SkOSFile_brew.cpp
- *
- * Copyright 2006, The Android Open Source Project
- * Copyright 2009, Company 100, Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-
-#include "SkOSFile.h"
-
-#ifdef SK_BUILD_FOR_BREW
-
-#include <AEEAppGen.h>
-#include <AEEFile.h>
-#include <AEEStdLib.h>
-
-SkFILE* sk_fopen(const char path[], SkFILE_Flags flags)
-{
-    int err;
-    OpenFileMode mode;
-    IFileMgr* fileMgr;
-    IFile* file;
-    IShell* shell;
-
-    shell = reinterpret_cast<AEEApplet*>(GETAPPINSTANCE())->m_pIShell;
-    err = ISHELL_CreateInstance(shell, AEECLSID_FILEMGR, (void**)&fileMgr);
-    if (err!= SUCCESS)
-        return NULL;
-
-    if (flags & kWrite_SkFILE_Flag)
-        mode = _OFM_READWRITE;
-    else /* kRead_SkFILE_Flag */
-        mode = _OFM_READ;
-
-    file = IFILEMGR_OpenFile(fileMgr, path, mode);
-    IFILEMGR_Release(fileMgr);
-
-    return (SkFILE*)file;
-}
-
-size_t sk_fgetsize(SkFILE* f)
-{
-    FileInfo fileInfo;
-
-    IFILE_GetInfo((IFile*)f, &fileInfo);
-    return fileInfo.dwSize;
-}
-
-bool sk_frewind(SkFILE* f)
-{
-    SkASSERT(f);
-    return IFILE_Seek((IFile*)f,  _SEEK_START, 0) == SUCCESS;
-}
-
-size_t sk_fread(void* buffer, size_t byteCount, SkFILE* f)
-{
-    SkASSERT(f);
-    if (buffer == NULL)
-    {
-        int err = IFILE_Seek((IFile*)f, _SEEK_CURRENT, (int)byteCount);
-        if (err == EFAILED) {
-            SkDEBUGF(("sk_fread: IFILE_Seek(%d) failed returned:%d\n", byteCount, err));
-            return 0;
-        }
-        return byteCount;
-    }
-    else
-        return IFILE_Read((IFile*)f, buffer, byteCount);
-}
-
-size_t sk_fwrite(const void* buffer, size_t byteCount, SkFILE* f)
-{
-    SkASSERT(f);
-    return IFILE_Write((IFile*)f, buffer, byteCount);
-}
-
-void sk_fflush(SkFILE* f)
-{
-    SkASSERT(f);
-}
-
-void sk_fclose(SkFILE* f)
-{
-    SkASSERT(f);
-    IFILE_Release((IFile*)f);
-}
-
-#endif
diff --git a/src/ports/SkOSFile_none.cpp b/src/ports/SkOSFile_none.cpp
index e22d22e..f268e13 100644
--- a/src/ports/SkOSFile_none.cpp
+++ b/src/ports/SkOSFile_none.cpp
@@ -13,6 +13,10 @@
 
 void sk_fmunmap(const void* addr, size_t length) { }
 
+void* sk_fdmmap(int fd, size_t* size) {
+    return NULL;
+}
+
 void* sk_fmmap(SkFILE* f, size_t* size) {
     return NULL;
 }
diff --git a/src/ports/SkOSFile_posix.cpp b/src/ports/SkOSFile_posix.cpp
index c9da4db..72a1b59 100644
--- a/src/ports/SkOSFile_posix.cpp
+++ b/src/ports/SkOSFile_posix.cpp
@@ -7,6 +7,8 @@
 
 #include "SkOSFile.h"
 
+#include "SkTemplates.h"
+
 #include <stdio.h>
 #include <sys/mman.h>
 #include <sys/stat.h>
@@ -42,16 +44,18 @@
     munmap(const_cast<void*>(addr), length);
 }
 
-void* sk_fmmap(SkFILE* f, size_t* size) {
-    size_t fileSize = sk_fgetsize(f);
-    if (0 == fileSize) {
+void* sk_fdmmap(int fd, size_t* size) {
+    struct stat status;
+    if (0 != fstat(fd, &status)) {
         return NULL;
     }
-
-    int fd = fileno((FILE*)f);
-    if (fd < 0) {
+    if (!S_ISREG(status.st_mode)) {
         return NULL;
     }
+    if (!SkTFitsIn<size_t>(status.st_size)) {
+        return NULL;
+    }
+    size_t fileSize = static_cast<size_t>(status.st_size);
 
     void* addr = mmap(NULL, fileSize, PROT_READ, MAP_PRIVATE, fd, 0);
     if (MAP_FAILED == addr) {
@@ -61,3 +65,16 @@
     *size = fileSize;
     return addr;
 }
+
+int sk_fileno(SkFILE* f) {
+    return fileno((FILE*)f);
+}
+
+void* sk_fmmap(SkFILE* f, size_t* size) {
+    int fd = sk_fileno(f);
+    if (fd < 0) {
+        return NULL;
+    }
+    
+    return sk_fdmmap(fd, size);
+}
diff --git a/src/ports/SkOSFile_win.cpp b/src/ports/SkOSFile_win.cpp
index fdf9ca5..2133f7b 100644
--- a/src/ports/SkOSFile_win.cpp
+++ b/src/ports/SkOSFile_win.cpp
@@ -7,6 +7,8 @@
 
 #include "SkOSFile.h"
 
+#include "SkTemplates.h"
+
 #include <io.h>
 #include <stdio.h>
 #include <sys/stat.h>
@@ -65,22 +67,21 @@
     UnmapViewOfFile(addr);
 }
 
-void* sk_fmmap(SkFILE* f, size_t* length) {
-    size_t fileSize = sk_fgetsize(f);
-    if (0 == fileSize) {
-        return NULL;
-    }
-
-    int fileno = _fileno((FILE*)f);
-    if (fileno < 0) {
-        return NULL;
-    }
-
+void* sk_fdmmap(int fileno, size_t* length) {
     HANDLE file = (HANDLE)_get_osfhandle(fileno);
     if (INVALID_HANDLE_VALUE == file) {
         return NULL;
     }
 
+    LARGE_INTEGER fileSize;
+    if (0 == GetFileSizeEx(file, &fileSize)) {
+        //TODO: use SK_TRACEHR(GetLastError(), "Could not get file size.") to report.
+        return NULL;
+    }
+    if (!SkTFitsIn<size_t>(fileSize.QuadPart)) {
+        return NULL;
+    }
+
     SkAutoWinMMap mmap(CreateFileMapping(file, NULL, PAGE_READONLY, 0, 0, NULL));
     if (!mmap.isValid()) {
         //TODO: use SK_TRACEHR(GetLastError(), "Could not create file mapping.") to report.
@@ -94,6 +95,19 @@
         return NULL;
     }
 
-    *length = fileSize;
+    *length = static_cast<size_t>(fileSize.QuadPart);
     return addr;
 }
+
+int sk_fileno(SkFILE* f) {
+    return _fileno((FILE*)f);
+}
+
+void* sk_fmmap(SkFILE* f, size_t* length) {
+    int fileno = sk_fileno(f);
+    if (fileno < 0) {
+        return NULL;
+    }
+
+    return sk_fdmmap(fileno, length);
+}
diff --git a/src/utils/win/SkDWriteFontFileStream.cpp b/src/utils/win/SkDWriteFontFileStream.cpp
index 971fdb9..eb59113 100644
--- a/src/utils/win/SkDWriteFontFileStream.cpp
+++ b/src/utils/win/SkDWriteFontFileStream.cpp
@@ -8,6 +8,7 @@
 #include "SkTypes.h"
 #include "SkDWriteFontFileStream.h"
 #include "SkHRESULT.h"
+#include "SkTemplates.h"
 #include "SkTScopedComPtr.h"
 
 #include <dwrite.h>
@@ -111,7 +112,7 @@
     HRESULT hr = S_OK;
     UINT64 realFileSize = 0;
     hr = fFontFileStream->GetFileSize(&realFileSize);
-    if (realFileSize > (std::numeric_limits<size_t>::max)()) {
+    if (!SkTFitsIn<size_t>(realFileSize)) {
         return 0;
     }
     return static_cast<size_t>(realFileSize);
diff --git a/tests/DataRefTest.cpp b/tests/DataRefTest.cpp
index 0cbce3f..d8bd24b 100644
--- a/tests/DataRefTest.cpp
+++ b/tests/DataRefTest.cpp
@@ -9,21 +9,10 @@
 #include "SkData.h"
 #include "SkDataSet.h"
 #include "SkDataTable.h"
-#include "SkStream.h"
 #include "SkOrderedReadBuffer.h"
 #include "SkOrderedWriteBuffer.h"
-
-template <typename T> class SkTUnref {
-public:
-    SkTUnref(T* ref) : fRef(ref) {}
-    ~SkTUnref() { fRef->unref(); }
-
-    operator T*() { return fRef; }
-    operator const T*() { return fRef; }
-
-private:
-    T*  fRef;
-};
+#include "SkOSFile.h"
+#include "SkStream.h"
 
 static void test_is_equal(skiatest::Reporter* reporter,
                           const SkDataTable* a, const SkDataTable* b) {
@@ -223,7 +212,7 @@
 
 static void test_dataset(skiatest::Reporter* reporter) {
     SkDataSet set0(NULL, 0);
-    SkDataSet set1("hello", SkTUnref<SkData>(SkData::NewWithCString("world")));
+    SkDataSet set1("hello", SkAutoTUnref<SkData>(SkData::NewWithCString("world")));
 
     const SkDataSet::Pair pairs[] = {
         { "one", SkData::NewWithCString("1") },
@@ -270,6 +259,40 @@
     REPORTER_ASSERT(reporter, 0 == *r2->bytes());
 }
 
+static void test_files(skiatest::Reporter* reporter) {
+    if (skiatest::Test::GetTmpDir().isEmpty()) {
+        return;
+    }
+    
+    const char* tmpDir = skiatest::Test::GetTmpDir().c_str();
+    SkString path;
+    path.printf("%s%s", tmpDir, "data_test");
+    
+    const char s[] = "abcdefghijklmnopqrstuvwxyz";
+    {
+        SkFILEWStream writer(path.c_str());
+        if (!writer.isValid()) {
+            SkString msg;
+            msg.printf("Failed to create tmp file %s\n", path.c_str());
+            reporter->reportFailed(msg.c_str());
+            return;
+        }
+        writer.write(s, 26);
+    }
+
+    SkFILE* file = sk_fopen(path.c_str(), kRead_SkFILE_Flag);
+    SkAutoTUnref<SkData> r1(SkData::NewFromFILE(file));
+    REPORTER_ASSERT(reporter, r1.get() != NULL);
+    REPORTER_ASSERT(reporter, r1->size() == 26);
+    REPORTER_ASSERT(reporter, strncmp(static_cast<const char*>(r1->data()), s, 26) == 0);
+    
+    int fd = sk_fileno(file);
+    SkAutoTUnref<SkData> r2(SkData::NewFromFD(fd));
+    REPORTER_ASSERT(reporter, r2.get() != NULL);
+    REPORTER_ASSERT(reporter, r2->size() == 26);
+    REPORTER_ASSERT(reporter, strncmp(static_cast<const char*>(r2->data()), s, 26) == 0);
+}
+
 static void TestData(skiatest::Reporter* reporter) {
     const char* str = "We the people, in order to form a more perfect union.";
     const int N = 10;
@@ -297,6 +320,7 @@
 
     test_cstring(reporter);
     test_dataset(reporter);
+    test_files(reporter);
 }
 
 #include "TestClassDef.h"