# Libipfsclient c library - design/braindump ## C library API This is what users of this library would see. We try to mimic posix but won't go the full way. Apps, like ffmpeg, use file descriptors and the `FILE` object. We just don't have that as a client library. ## Usecase C/C++ based applications might find it far easier to implement IPFS support when it has a "file-based" that abstracts away all the IPFS specifics. Applications like ffmpeg, curl, kodi could link to this library and implement IPFS support that way. IOT devices like ESP8266 or Raspberry pi, etc. Another potential interesting usecase is for this library to serve as basis for the later to-be-developed cross-platform FUSE library. ## How it should look (old idea) When using this library, reading a file from IPFS should look like this: ```clike= #include <libipfsclient.h> int main () { IPFSObj ipfs; int c; ipfs.open("ipfs://<cid>","r"); while(1) { c = ipfs.getc(); if (ipfs.eof()) { break; } printf("%c", c); } ipfs.close(); return(0); } ``` There's much more to it then this, but this should give a general idea about it's use and API similarity when compared to posix file operations. ## How it should look (22-09-2022) This is a little updated with actual working code, kind of: ```clike= #include <libipfsclient.h> int main () { unsigned char buffer[1024]; size_t bytesRead = 0; IPFSObj ipfs; IPFSObjConstruct(&ipfs); ipfs.open(&ipfs, "ipfs://<cid>"); while ((bytesRead = ipfs.read(&ipfs, buffer, 1, sizeof(buffer), bytesRead)) > 0) { // Do what you want with buffer } ipfs.close(&ipfs); return(0); } ``` ## Which Posix-like functions are needed? For this I just look at the ffmpeg source for how they open a file ([here](https://github.com/FFmpeg/FFmpeg/blob/master/libavformat/file.c) and [here](https://github.com/FFmpeg/FFmpeg/blob/master/libavutil/file_open.c)). Do note that they made wrapper for some file operations, making it more difficult to find. * fdopen * read * fstat * lseek * close ## File API differences in posix vs nodejs | Function | Posix/C | Nodejs | API (with libcurl) | | -------- | -------- | -------- | -------- | | Open file | fopen | <fs>.open |IPFSObj.open| | Read x data | fread | filehandle.read |IPFSObj.read| | Set point to read from | fseek | filehandle.read |IPFSObj.read| | Close file | fclose | filehandle.close |IPFSObj.close| | Stat | fstat | filehandle.stat |none| Do note that the context here, as a proof of concept, is to use this API in ffmpeg. The reasoning used here doesn't apply when you want to list files. ### Open In posix, open more or less, registers an index (your `file descriptor`, FD). It does some checking (like file exists) and gives you an FD or an error code. Nodejs acts mostly the same. In the **API** open means a web request to a gateway and based on it's response it either works or doesn't. There are no "file descriptors" in the classical sense though the `IPFSObj` could be seen as one. ### Seeking In posix seeking is done by calling `fseek` on the file handle and setting the position to somewhere in your datastream. A subsequent call to `fread` will then read from that position. In nodejs there is no such function to seek. It's build into the `read` function as one of the arguments. In the **API** I'm trying to follow both posix and curl here. Meaning the read function in the API has an extra argument (when compared to posix) to set the seek point. This means that there is no `seek` in the API (yet). ### Stat Within the context of this proof of concept a stat call is pointless. In the posix world the stat call is used to determine of a file exists and what kind of type it is. ## Future sidenote While having this library is a win regardless, using it as base for the cross-platform FUSE attempt might be harder then it needs to be. In Go it's probably (assumption) easier to do web requests directly then to rely on a C library. But avoiding code duplication would argue in favor for using the C library still. To be determined what's best here. ## How it should look (@Jorropo) ```c // Error handling omited for clarity unsigned char buffer[1024]; FeatherIPFS feather; featherStart(&feather, "Qmfoo/a/b/c", 1000, -1); // the two last item are start and end ranges while (1) { auto todo = featherTodo(&feather); switch (todo.typ){ case feather_done: goto requestOver; case feather_new_HTTP_request: char* [2]header; header[1] = NULL; // NULL terminated header list switch (todo.req.typ) { case feather_block: header[0] = "Accept: application/vnd.ipfs.raw"; break; case feather_car: header[0] = "Accept: application/vnd.ipfs.car"; break; default: __builtin_unreachable(); } httpRequest object; object.headers = header; object.path = todo.req.path; // /ipfs/CID ... object.netloc = gateway; // https://ipfs.example.com unsigned char httpbuffer[1024]; while (1) { unsigned char doneHttp; long int red = requestRead(&object, &done, httpbuffer, sizeof(httpbuffer)); unsigned char done; red = featherReceive(&feather, &done, doneHttp, httpbuffer, red, buffer, sizeof(buffer)); // Here you can consume buffer[:red] // note red will be 0 while we are doing the first resolution (walking the directory and walking into the right offsets) if (done) { break; } } default: __builtin_unreachable(); } } requestOver: ```