Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add FileUtils::getContents(). #15479

Merged
merged 37 commits into from
Apr 26, 2016
Merged

Add FileUtils::getContents(). #15479

merged 37 commits into from
Apr 26, 2016

Conversation

xpol
Copy link
Contributor

@xpol xpol commented Apr 21, 2016

Why another file read API?

Current APIs has some problem:

  1. FileUtils::getDataFormFile() can't return string, convert from Data to string will make extra copy.
  2. FileUtils::getStringFormFile() can't read binary file, and it truncate contents.
  3. FileUtils::getDataFormFile() and FileUtils::getStringFormFile() does not tell different from empty file and io errors.

Advantage of FileUtils::getContents()

  1. Support read into cocos2d::Data std::basic_string and std::vector
  2. It has abstract of the output buffer, custom type of buffer object can be adapted by writing subclass ResizableBuffer.
  3. It returns error code, so that we can know the different form empty file and io error.

typename T,
typename std::enable_if<
std::is_base_of< ResizableBuffer, ResizableBufferAdapter<T> >::value
>::type* = nullptr
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know the effect of enable_if<>, but i don't know the effect of

typename std::enable_if<xx>::type* = nullptr

What does it mean? I can not find the document for it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

http://en.cppreference.com/w/cpp/types/enable_if See Example section #4 usage.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, i have seen it, but don't understand, why can not use

typename std::enable_if<xx>::type

Copy link
Contributor Author

@xpol xpol Apr 21, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will cause Substitution Failure if ResizableBufferAdapter<T> is not a subcalss of ResizableBuffer.

I only defined ResizableBufferAdapter< std::vector<> > ResizableBufferAdapter< std::string<> > and ResizableBufferAdapter< Data > inherited form ResizableBuffer. So the template version of getContents() will enabled only for std::vector<> std::string<> and Data.

If user defined his own version ResizableBufferAdapter< UserType >, that type UserType will also suitable for calling template version of getContents().

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know the meaning, but don't understand why need to assign to nullptr. Ok, i will do more research about it.

Thanks.

Copy link
Contributor Author

@xpol xpol Apr 21, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

template <
        typename T,
        typename X = std::enable_if<
            std::is_base_of< ResizableBuffer, ResizableBufferAdapter<T> >::value
        >::type
>

will not compile:

CCFileUtils.h:206:22: error: need 'typename' before 'std::enable_if<std::is_base_of<cocos2d::ResizableBuffer, cocos2d::ResizableBufferAdapter<T> >::value>::type' because 'std::enable_if<std::is_base_of<cocos2d::ResizableBuffer, cocos2d::ResizableBufferAdapter<T> >::value>' is a dependent scope
         typename X = std::enable_if<
                      ^

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it, thanks.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But the #5 section of http://en.cppreference.com/w/cpp/types/enable_if can work.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This works:

template <
        typename T,
        typename X = typename std::enable_if<
            std::is_base_of< ResizableBuffer, ResizableBufferAdapter<T> >::value
        >::type
>

Copy link
Contributor Author

@xpol xpol Apr 21, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#5 section of http://en.cppreference.com/w/cpp/types/enable_if uses enable_if_t (note the tailling _t ) is a C++14 feature.

@minggo
Copy link
Contributor

minggo commented Apr 21, 2016

I found only Android platform implements FileError getContents(const std::string& filename, ResizableBuffer* buffer), what will happen if it is invoked in other platforms?

@xpol
Copy link
Contributor Author

xpol commented Apr 21, 2016

There is default: FileError FileUtils::getContents(const std::string& filename, ResizableBuffer* buffer) in CCFileUtils.cpp.

@minggo
Copy link
Contributor

minggo commented Apr 21, 2016

Sorry about my careless. I will continue viewing it tomorrow.

@minggo minggo self-assigned this Apr 21, 2016
@xpol
Copy link
Contributor Author

xpol commented Apr 21, 2016

@minggo
Thank you very much.

template<typename T, typename A>
class ResizableBufferAdapter< std::vector<T, A> > : public ResizableBuffer {
typedef std::vector<T, A> BufferType;
BufferType* buffer_;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

buffer_ -> _buffer to obey the cocos2d-x coding style

@minggo
Copy link
Contributor

minggo commented Apr 22, 2016

I run the test case on Mac, it said getContents() failed: error: sbuf != dbuf.

And can we use FileUtils::getContents() to implement FileUtils::getDataFromFile() and FileUtils::getStringFromFile()? Then it will remove duplicated codes.

@xpol
Copy link
Contributor Author

xpol commented Apr 22, 2016

FileUtils::getStringFromFile() is actually bad:

Does the new code of FileUtils::getStringFromFile() needs to implement the truncate and convert CRLF?
For convert CRLF, do I need to convert CRLF only for Windows (that is the current behavior) ?

@minggo
Copy link
Contributor

minggo commented Apr 22, 2016

@xpol What i mean is use FileUtils::getContents() internally in FileUtils::getStringFromFile(), for example

 FileUtils::getStringFromFile()
{
    ...
    getContents(...); // use getContents() to get needed result
}

@xpol
Copy link
Contributor Author

xpol commented Apr 22, 2016

Yes.

  • Does the new code of FileUtils::getStringFromFile() needs to implement the truncate and convert CRLF?
  • For converting CRLF, do I need to convert CRLF only for Windows (that is the current behavior) ?

@minggo
Copy link
Contributor

minggo commented Apr 22, 2016

@xpol I think it is better to keep compatibility and fix bugs in other pull request.

public:
explicit ResizableBufferAdapter(BufferType* buffer) : _buffer(buffer) {}
virtual void resize(size_t size) override {
_buffer->resize((size + sizeof(C) - 1) / sizeof(C));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the size is (size + sizeof(C) - 1) / sizeof(C)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because that the char type for string and member type for vector may not 8 bit.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But i think std::basic_string<> has already handles it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

std::basic_string<>::size() and std::basic_string<>::resize() is talking about the number of elements (yes the name make users think it's bytes size).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So the size here is not the number of element? Why?

Copy link
Contributor Author

@xpol xpol Apr 22, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ResizableBuffer::resize() is about file bytes.

When C is 8 bit. std::basic_string<char>::size() == file bytes
When C is not 8 bit: std::basic_string<wchar_t>::size() != file bytes

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What i mean is why size in ResizableBuffer::resize() is bytes. If you want to read wchar_t, then developers should calculate the size of bytes itself?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ResizableBuffer::resize() is not for user calling, FileUtils::getContents() calls it internally:

  1. ResizableBuffer::resize() is called by ``at cocos/platform/CCFileUtils.cpp[line 687](https://github.com/cocos2d/cocos2d-x/pull/15479/files#diff-cb65223cd3edf30671fefad4a1883edfR687) inFileUtils::getContents()` when the file size is determined, and thus, the file size is passed to `ResizableBuffer::resize()`.
  2. ResizableBufferAdapter convert bytes to number of elements need.

Example Usage:

    std::string<char32_t> buf;
    fs->getContents(file, &buf); // resize buf automatically inside getContents()
    std::cout << "I got " << buf.size() << " of wchar32_t" << std::endl;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it, thanks.

methods are:

* FileUtils::getDataFromFile()
* FileUtils::getStringFromFile()
* FileUtils::getFileData()

and follow Android methods are now just calls FileUtils' ones.

* FileUtilsAndroid::getDataFromFile()
* FileUtilsAndroid::getStringFromFile()
* FileUtilsAndroid::getFileData()
@xpol
Copy link
Contributor Author

xpol commented Apr 22, 2016

@minggo
Shall I remove follow API from FileUtilsAndroid as they are not necessary now (4c6e4dc):

  • FileUtilsAndroid::getDataFromFile()
  • FileUtilsAndroid::getStringFromFile()
  • FileUtilsAndroid::getFileData()

{
// TODO: move this function to StringUtils.
// http://stackoverflow.com/a/7724536/87021
static inline std::string replaceAll(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think static is not need here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@xpol I think static is not needed when it is in an anonymous namespace.

@xpol
Copy link
Contributor Author

xpol commented Apr 25, 2016

@minggo
Have you got any idea about the Linux build error:

In file included from /linux_slave/workspace/ccs-pr/node/linux/cocos/platform/linux/CCDevice-linux.cpp:30:0:
/linux_slave/workspace/ccs-pr/node/linux/cocos/platform/CCFileUtils.h:164:16: error: anonymous scoped enum is not allowed
     enum class Status
                ^
In file included from /linux_slave/workspace/ccs-pr/node/linux/cocos/platform/linux/CCDevice-linux.cpp:39:0:
/linux_slave/workspace/ccs-pr/node/linux/cocos/platform/CCFileUtils.h:164:5: warning: elaborated-type-specifier for a scoped enum must not use the ‘class’ keyword
     enum class Status
     ^
In file included from /linux_slave/workspace/ccs-pr/node/linux/cocos/platform/linux/CCDevice-linux.cpp:30:0:
/linux_slave/workspace/ccs-pr/node/linux/cocos/platform/CCFileUtils.h:164:16: error: expected identifier before ‘int’
     enum class Status
                ^
In file included from /linux_slave/workspace/ccs-pr/node/linux/cocos/platform/linux/CCDevice-linux.cpp:39:0:
/linux_slave/workspace/ccs-pr/node/linux/cocos/platform/CCFileUtils.h:165:5: error: expected unqualified-id before ‘{’ token
     {
     ^
cocos/CMakeFiles/cocos2dInternal.dir/build.make:2607: recipe for target 'cocos/CMakeFiles/cocos2dInternal.dir/platform/linux/CCDevice-linux.cpp.o' failed

@minggo
Copy link
Contributor

minggo commented Apr 25, 2016

Remove the last ,?

@minggo
Copy link
Contributor

minggo commented Apr 25, 2016

About the linux error, i think it is because some header files define a macro named Status, such as

#define Status

@xpol
Copy link
Contributor Author

xpol commented Apr 25, 2016

Yes, I have moved the include of CCFileUtils.h up in cocos\platform\linux\CCDevice-linux.cpp

@xpol
Copy link
Contributor Author

xpol commented Apr 26, 2016

ping

bool textmode = strchr(mode, 't') != NULL;
if (textmode)
textFormCRLF(s);
#endif
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these codes should be applied to WIN32 too. Sorry for my previous answer about it. And NULL -> nullptr

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to comments above in our current code base :

  • For FileUtils::getStringFromFile() : do not convert CRLF to LF on all platform including Win32 and WinRT.
  • For FileUtils::getFileData() : Only WinRT do CRLF to LF conversion ( mode arg is not used on Win32 ). And I guess this is a bug.

Did you mean in this PR, we shall do support CRLF to LF on Win32 and WinRT for both FileUtils::getStringFromFile() and FileUtils::getFileData()?

IMHO, we shold remove all support of CRLF to LF behavior as:

  • it's not portable (and cocos2d-x is a cross platform engine).
  • currently only FileUtils::getFileData() on WinRT support CRLF to LF.
  • all other case (non WinRT or not FileUtils::getFileData()), the current behavior is not convert CRLF to LF.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, i misunderstood of the logic of textFormCRLF. Yep, i agree with you, we should not convert CRLF to LF. So i think we can remove these codes.

@minggo
Copy link
Contributor

minggo commented Apr 26, 2016

@xpol I added comments above. I think i can merge it when it is resolved. Thanks.

@xpol
Copy link
Contributor Author

xpol commented Apr 26, 2016

@minggo

I updated the line note, I just want to make it sure.

Would please inspect the the links in above line note link to current v3 code if you needs detailed information.

@minggo
Copy link
Contributor

minggo commented Apr 26, 2016

@xpol I think we can remove the codes

+#if CC_TARGET_PLATFORM == CC_PLATFORM_WINRT
+    // TODO: only winrt support text mode. it is possibly a bug. if true, remove the follow code for text mode.
+    bool textmode = strchr(mode, 't') != NULL;
+    if (textmode)
+        textFormCRLF(s);
+#endif

And static is not needed if a function is in an anonymous namespace.

@xpol
Copy link
Contributor Author

xpol commented Apr 26, 2016

OK then all platform has the same behavior: no text mode support for file reading.

BTW, the static and NULL issues is gone as they are in the CRLF to LF support code.

@minggo
Copy link
Contributor

minggo commented Apr 26, 2016

Thanks. I think it is ok to do it like this. And let's fix it if we met issues in future.

@xpol
Copy link
Contributor Author

xpol commented Apr 26, 2016

Glad I can help.

@minggo minggo merged commit 43a94c0 into cocos2d:v3 Apr 26, 2016
@minggo minggo added this to the next milestone Apr 26, 2016
@xpol xpol deleted the file-utils-get-contents branch April 26, 2016 05:59
stevetranby pushed a commit to stevetranby/cocos2d-x that referenced this pull request May 3, 2016
* Add FileUtils::getContents().

* skip FileUtils::getContents() in binding generator config.

* use FileUtils::getContents in CCDataReaderHelper.

* obey the cocos2d-x coding style.

* Explicit constructor.

* More docs.

* More tests.

* Move FileError to FileUtils::Error.

* Fixes wrong buffer size for reading into string and vector.

* Update tests.

* Add note on padding for output buffers.

* FileUtils: implements old methods by using `getContents()`.

methods are:

* FileUtils::getDataFromFile()
* FileUtils::getStringFromFile()
* FileUtils::getFileData()

and follow Android methods are now just calls FileUtils' ones.

* FileUtilsAndroid::getDataFromFile()
* FileUtilsAndroid::getStringFromFile()
* FileUtilsAndroid::getFileData()

* Fixes build error.

* FileUtils::getFileData: Return the size of data.

* Remove old methods form FileUtilsAndroid they are now done in FileUtils.

* Fixes for win32 code.

* Fixes build error in test and add more test.

* Better error message.

* Make template type name more readable.

* Update comments.

* Move internal functions to anonymous namespace.

* Refactor FileUtils test.

* Fix warning about compare signed and unsigned.

* Win32 and WinRT does not use text mode.

That is we don't need simulate convert CRLF to LF.

* Fixes for Win32 and WinRT.

* Update for Win32 and WinRT.

* Win32: return FileUtils:Error::TooLarge when file is large than 2^32-1.
* Win32: remove checkFileName() which has no effect at all.
* WinRT: add FileUtilsWinRT::getContents() using ::CreateFile2.
* WinRT: add override keyword for FileUtilsWinRT::getFileSize().

* Update for coding styles.

* More error strings.

* check read and malloc return codes.

* rename FileUtils::Error to FileUtils::Status.

* Fixes for WinRT, use GetFileInformationByHandleEx to get file size.

* Fixes build error for winrt and cleanup FileUtils::Status.

* Try to fix the build error on Linux.

Status must defined in some header, so move FileUtils.h up.

* Remove support of text mode on WinRT (it is the last platform support text mode).
stevetranby pushed a commit to stevetranby/cocos2d-x that referenced this pull request May 4, 2016
* Add FileUtils::getContents().

* skip FileUtils::getContents() in binding generator config.

* use FileUtils::getContents in CCDataReaderHelper.

* obey the cocos2d-x coding style.

* Explicit constructor.

* More docs.

* More tests.

* Move FileError to FileUtils::Error.

* Fixes wrong buffer size for reading into string and vector.

* Update tests.

* Add note on padding for output buffers.

* FileUtils: implements old methods by using `getContents()`.

methods are:

* FileUtils::getDataFromFile()
* FileUtils::getStringFromFile()
* FileUtils::getFileData()

and follow Android methods are now just calls FileUtils' ones.

* FileUtilsAndroid::getDataFromFile()
* FileUtilsAndroid::getStringFromFile()
* FileUtilsAndroid::getFileData()

* Fixes build error.

* FileUtils::getFileData: Return the size of data.

* Remove old methods form FileUtilsAndroid they are now done in FileUtils.

* Fixes for win32 code.

* Fixes build error in test and add more test.

* Better error message.

* Make template type name more readable.

* Update comments.

* Move internal functions to anonymous namespace.

* Refactor FileUtils test.

* Fix warning about compare signed and unsigned.

* Win32 and WinRT does not use text mode.

That is we don't need simulate convert CRLF to LF.

* Fixes for Win32 and WinRT.

* Update for Win32 and WinRT.

* Win32: return FileUtils:Error::TooLarge when file is large than 2^32-1.
* Win32: remove checkFileName() which has no effect at all.
* WinRT: add FileUtilsWinRT::getContents() using ::CreateFile2.
* WinRT: add override keyword for FileUtilsWinRT::getFileSize().

* Update for coding styles.

* More error strings.

* check read and malloc return codes.

* rename FileUtils::Error to FileUtils::Status.

* Fixes for WinRT, use GetFileInformationByHandleEx to get file size.

* Fixes build error for winrt and cleanup FileUtils::Status.

* Try to fix the build error on Linux.

Status must defined in some header, so move FileUtils.h up.

* Remove support of text mode on WinRT (it is the last platform support text mode).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants