Initial community commit

This commit is contained in:
Jef
2024-09-24 14:54:57 +02:00
parent 537bcbc862
commit 20d28e80a5
16810 changed files with 4640254 additions and 2 deletions

View File

@ -0,0 +1,525 @@
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include "NXFileObject.h"
#include "nx/nxfile.h"
#include "nu/nodelist.h"
#include "foundation/atomics.h"
#include "nu/nodelist.h"
#include <errno.h>
#include <new>
/* Windows implementation
note: for now, we're using FILE. We will eventually replace with better buffering
TODO: deal with files opened in "append" mode */
NXFileObject::NXFileObject()
{
nodelist_init(&region_stack);
region.start = 0;
region.end = 0xFFFFFFFFFFFFFFFFULL;
position=0;
reference_count=1;
uri=0;
memset(&file_stats, 0, sizeof(file_stats));
}
NXFileObject::~NXFileObject()
{
NXURIRelease(uri);
NXFileRegion *region = (NXFileRegion *)region_stack.head;
while (region)
{
NXFileRegion *next = (NXFileRegion *)region->Next;
free(region);
region = next;
}
}
ns_error_t NXFileObject::Initialize(nx_uri_t _uri)
{
uri = NXURIRetain(_uri);
return NErr_Success;
}
size_t NXFileObject::Retain()
{
return nx_atomic_inc(&reference_count);
}
size_t NXFileObject::Release()
{
if (!reference_count)
{
return reference_count;
}
size_t r = nx_atomic_dec_release(&reference_count);
if (!r)
{
delete(this);
}
return r;
}
ns_error_t NXFileObject::LockRegion(uint64_t start, uint64_t end)
{
// save the old region data
NXFileRegion *old_region = (NXFileRegion *)calloc(1, sizeof(NXFileRegion));
if (!old_region)
{
return NErr_OutOfMemory;
}
old_region->start = region.start;
old_region->end = region.end;
nodelist_push_front(&region_stack, old_region);
// if we're already locked, Lock within our current region.
// The weird way the logic is done prevents overflow
if (start > region.end - region.start)
{
start = region.end;
}
else
{
start = region.start + start;
}
if (end > region.end - region.start)
{
end = region.end;
}
else
{
end = region.start + end;
}
region.start = start;
region.end = end;
return NErr_Success;
}
ns_error_t NXFileObject::UnlockRegion()
{
NXFileRegion *new_region = (NXFileRegion *)nodelist_pop_front(&region_stack);
if (new_region)
{
region.start = new_region->start;
region.end = new_region->end;
free(new_region);
return NErr_Success;
}
else
{
return NErr_NoAction;
}
}
ns_error_t NXFileObject::Stat(nx_file_stat_t out_stats)
{
*out_stats = file_stats;
return NErr_Success;
}
ns_error_t NXFileObject::Length(uint64_t *length)
{
*length = region.end - region.start;
return NErr_Success;
}
ns_error_t NXFileObject::EndOfFile()
{
if (position >= region.end)
{
return NErr_True;
}
else
{
return NErr_False;
}
}
/* ----------------------------------------- */
class NXFileObject_FILE : public NXFileObject
{
public:
NXFileObject_FILE();
~NXFileObject_FILE();
ns_error_t Initialize(nx_uri_t uri, FILE *f, bool writable);
private:
FILE *f;
bool writable;
ns_error_t Read(void *buffer, size_t bytes_requested, size_t *bytes_read);
ns_error_t Write(const void *buffer, size_t bytes);
ns_error_t Seek(uint64_t position);
ns_error_t Tell(uint64_t *position);
ns_error_t PeekByte(uint8_t *byte);
ns_error_t Sync();
ns_error_t Truncate();
};
NXFileObject_FILE::NXFileObject_FILE()
{
f = 0;
writable = false;
}
NXFileObject_FILE::~NXFileObject_FILE()
{
if (f)
{
fclose(f);
}
}
ns_error_t NXFileObject_FILE::Initialize(nx_uri_t uri, FILE *_f, bool _writable)
{
writable = _writable;
ns_error_t ret = NXFileObject::Initialize(uri);
if (ret != NErr_Success)
{
return ret;
}
ret = NXFile_statFILE(_f, &file_stats);
if (ret != NErr_Success)
{
return ret;
}
region.end = file_stats.file_size;
f = _f;
return NErr_Success;
}
ns_error_t NXFileObject_FILE::Read(void *buffer, size_t bytes_requested, size_t *bytes_read)
{
// if it's an "empty" read, we need to determine whether or not we're at the end of the file
if (bytes_requested == 0)
{
if (region.end == position || feof(f))
{
return NErr_EndOfFile;
}
else
{
return NErr_Success;
}
}
// don't read into any data after the locked region
if ((uint64_t)bytes_requested > region.end - position)
{
bytes_requested = (size_t)(region.end - position);
}
if (bytes_requested == 0)
{
return NErr_EndOfFile;
}
if (buffer == 0)
{
uint64_t old_position=position;
Seek(position+bytes_requested);
if (bytes_read)
{
*bytes_read = (size_t)(position - old_position);
}
return NErr_Success;
}
else
{
size_t results = fread(buffer, 1, bytes_requested, f);
if (results == 0)
{
if (feof(f))
{
return NErr_EndOfFile;
}
else
{
return NErr_Error;
}
}
if (bytes_read)
{
*bytes_read = results;
}
position+=results;
return NErr_Success;
}
}
ns_error_t NXFileObject_FILE::Write(const void *buffer, size_t bytes)
{
// TODO: review this in relation to locked regions
size_t results = fwrite(buffer, 1, bytes, f);
if (results == 0)
{
return NErr_Error;
}
position += results;
if (region.end < position)
{
region.end = position;
}
return NErr_Success;
}
ns_error_t NXFileObject_FILE::PeekByte(uint8_t *byte)
{
if (position == region.end)
{
return NErr_EndOfFile;
}
int read_byte = fgetc(f);
if (read_byte != EOF)
{
ungetc(read_byte, f);
}
else
{
return NErr_EndOfFile;
}
*byte = (uint8_t)read_byte;
return NErr_Success;
}
ns_error_t NXFileObject_FILE::Seek(uint64_t new_position)
{
if (!writable)
{
// doing it this way will prevent integer overflow
if (new_position > (region.end - region.start))
{
new_position = region.end - region.start;
}
}
if (_fseeki64(f, region.start+new_position, SEEK_SET) == 0)
{
position = region.start+new_position;
return NErr_Success;
}
else
{
return NErr_Error;
}
}
ns_error_t NXFileObject_FILE::Tell(uint64_t *out_position)
{
*out_position = position - region.start;
return NErr_Success;
}
ns_error_t NXFileObject_FILE::Sync()
{
fflush(f);
return NErr_Success;
}
ns_error_t NXFileObject_FILE::Truncate()
{
int fd = _fileno(f);
_chsize_s(fd, position);
return NErr_Success;
}
/* ----------------------------------------- */
ns_error_t NXFileOpenFile(nx_file_t *out_file, nx_uri_t filename, nx_file_FILE_flags_t flags)
{
FILE *f = NXFile_fopen(filename, flags);
if (!f)
{
if (errno == ENOENT)
{
return NErr_FileNotFound;
}
else
{
return NErr_Error;
}
}
NXFileObject_FILE *file_object = new (std::nothrow) NXFileObject_FILE;
if (!file_object)
{
fclose(f);
return NErr_OutOfMemory;
}
ns_error_t ret = file_object->Initialize(filename, f, !!(flags & nx_file_FILE_writable_mask));
if (ret != NErr_Success)
{
fclose(f);
delete file_object;
return ret;
}
*out_file = (nx_file_t)file_object;
return NErr_Success;
}
nx_file_t NXFileRetain(nx_file_t _f)
{
if (!_f)
{
return 0;
}
NXFileObject *f = (NXFileObject *)_f;
f->Retain();
return _f;
}
void NXFileRelease(nx_file_t _f)
{
if (_f)
{
NXFileObject *f = (NXFileObject *)_f;
f->Release();
}
}
ns_error_t NXFileRead(nx_file_t _f, void *buffer, size_t bytes_requested, size_t *bytes_read)
{
if (!_f)
{
return NErr_BadParameter;
}
NXFileObject *f = (NXFileObject *)_f;
return f->Read(buffer, bytes_requested, bytes_read);
}
ns_error_t NXFileSeek(nx_file_t _f, uint64_t position)
{
if (!_f)
{
return NErr_BadParameter;
}
NXFileObject *f = (NXFileObject *)_f;
return f->Seek(position);
}
ns_error_t NXFileTell(nx_file_t _f, uint64_t *position)
{
if (!_f)
{
return NErr_BadParameter;
}
NXFileObject *f = (NXFileObject *)_f;
return f->Tell(position);
}
ns_error_t NXFileLockRegion(nx_file_t _f, uint64_t start_position, uint64_t end_position)
{
if (!_f)
{
return NErr_BadParameter;
}
NXFileObject *f = (NXFileObject *)_f;
return f->LockRegion(start_position, end_position);
}
ns_error_t NXFileUnlockRegion(nx_file_t _f)
{
if (!_f)
{
return NErr_BadParameter;
}
NXFileObject *f = (NXFileObject *)_f;
return f->UnlockRegion();
}
ns_error_t NXFileStat(nx_file_t _f, nx_file_stat_t file_stats)
{
if (!_f)
{
return NErr_BadParameter;
}
NXFileObject *f = (NXFileObject *)_f;
return f->Stat(file_stats);
}
ns_error_t NXFileLength(nx_file_t _f, uint64_t *length)
{
if (!_f)
{
return NErr_BadParameter;
}
NXFileObject *f = (NXFileObject *)_f;
return f->Length(length);
}
ns_error_t NXFileEndOfFile(nx_file_t _f)
{
if (!_f)
{
return NErr_BadParameter;
}
NXFileObject *f = (NXFileObject *)_f;
return f->EndOfFile();
}
ns_error_t NXFilePeekByte(nx_file_t _f, uint8_t *byte)
{
if (!_f)
{
return NErr_BadParameter;
}
NXFileObject *f = (NXFileObject *)_f;
return f->PeekByte(byte);
}
ns_error_t NXFileWrite(nx_file_t _f, const void *buffer, size_t bytes)
{
if (!_f)
{
return NErr_BadParameter;
}
NXFileObject *f = (NXFileObject *)_f;
return f->Write(buffer, bytes);
}
ns_error_t NXFileSync(nx_file_t _f)
{
if (!_f)
{
return NErr_BadParameter;
}
NXFileObject *f = (NXFileObject *)_f;
return f->Sync();
}
ns_error_t NXFileTruncate(nx_file_t _f)
{
if (!_f)
{
return NErr_BadParameter;
}
NXFileObject *f = (NXFileObject *)_f;
return f->Truncate();
}

View File

@ -0,0 +1,45 @@
#pragma once
#include "foundation/types.h"
#include "foundation/error.h"
#include "nx/nxuri.h"
#include "nx/nxfile.h"
#include "nu/nodelist.h"
struct NXFileRegion : public queue_node_t
{
uint64_t start;
uint64_t end;
};
class NXFileObject
{
public:
size_t Retain();
size_t Release();
ns_error_t LockRegion(uint64_t start, uint64_t end);
ns_error_t UnlockRegion();
ns_error_t Stat(nx_file_stat_t out_stats);
ns_error_t Length(uint64_t *length);
virtual ns_error_t Read(void *buffer, size_t bytes_requested, size_t *bytes_read)=0;
virtual ns_error_t Write(const void *buffer, size_t bytes)=0;
virtual ns_error_t PeekByte(uint8_t *byte)=0;
virtual ns_error_t Seek(uint64_t position)=0;
virtual ns_error_t Tell(uint64_t *position)=0;
virtual ns_error_t Sync()=0;
virtual ns_error_t Truncate()=0;
virtual ns_error_t EndOfFile();
protected:
NXFileObject();
virtual ~NXFileObject();
nx_file_stat_s file_stats;
uint64_t position; /* note: this represents absolute position, _not_ position within the region */
ns_error_t Initialize(nx_uri_t uri);
NXFileRegion region;
nodelist_s region_stack;
nx_uri_t uri;
volatile size_t reference_count;
};

View File

@ -0,0 +1,749 @@
#include "NXFileObject.h"
#include "nu/ProgressTracker.h"
#include "nx/nxthread.h"
#include "nx/nxsleep.h"
#include "jnetlib/jnetlib.h"
#include "../nswasabi/AutoCharNX.h"
#include "nswasabi/ReferenceCounted.h"
#include "nu/MessageLoop.h"
#include <time.h>
#include <new>
#include "../../../WAT/WAT.h"
/* TODO: benski> test this with a server that does not return content-length. I bet we could get it to work */
/* TODO: benski> on windows, we can use a single CreateFile HANDLE for both reading and writing
and use ReadFile(..., &overlapped) to maintain two separate file pointers
this should improve performance as they will share the same cache
_might_ have to use async I/O to get it to work (but use it synchronously by waiting on the handle after making the call
*/
#define HTTP_BUFFER_SIZE 65536
class NXFileObject_ProgressiveDownloader;
enum
{
MESSAGE_KILL,
MESSAGE_SEEK,
MESSAGE_SIZE,
MESSAGE_ERROR,
MESSAGE_CLOSED,
MESSAGE_CONNECTED,
};
char MessageString[6][10] =
{
"Kill",
"Seek",
"Size",
"Error",
"Closed",
"Connected"
};
struct seek_message_t : public nu::message_node_t
{
uint64_t start;
uint64_t end;
};
struct size_message_t : public nu::message_node_t
{
uint64_t size;
};
struct error_message_t : public nu::message_node_t
{
int error_code;
};
/* This class represents the thread that's actually downloading the content from the server */
class ProgressiveDownload
{
public:
ProgressiveDownload(ProgressTracker &progress_tracker, NXFileObject_ProgressiveDownloader &parent);
~ProgressiveDownload();
ns_error_t Initialize(nx_uri_t uri, jnl_http_t http, const char *user_agent, nx_uri_t temp_uri);
void Seek(uint64_t start, uint64_t end);
void Close();
private:
/* These functions are called on the local thread */
/* These functions run on the download thread */
static nx_thread_return_t NXTHREADCALL _ProgressiveThread(nx_thread_parameter_t param) { return ((ProgressiveDownload *)param)->ProgressiveThread(); }
nx_thread_return_t NXTHREADCALL ProgressiveThread();
int Connect();
void Internal_Write(const void *data, size_t data_len);
int Wait(int milliseconds);
ns_error_t SetupConnection(uint64_t start_position, uint64_t end_position);
int DoRead(void *buffer, size_t bufferlen);
void ProcessMessage(nu::message_node_t *message);
private:
ProgressTracker &progress_tracker;
NXFileObject_ProgressiveDownloader &parent;
nx_uri_t temp_filename, url;
FILE *progressive_file_write;
jnl_http_t http;
char *user_agent;
nx_thread_t download_thread;
nu::MessageLoop message_loop;
uint64_t file_size;
int killswitch;
};
class NXFileObject_ProgressiveDownloader: public NXFileObject
{
public:
NXFileObject_ProgressiveDownloader();
~NXFileObject_ProgressiveDownloader();
ns_error_t Initialize(nx_uri_t uri, jnl_http_t http, const char *user_agent);
bool Available(uint64_t size, uint64_t *available);
/* API used by ProgressiveDownload */
void OnFileSize(uint64_t filesize);
void OnConnected();
void OnError(int error_code);
void OnClosed();
private:
/* NXFileObject implementation */
ns_error_t Read(void *buffer, size_t bytes_requested, size_t *bytes_read);
ns_error_t Write(const void *buffer, size_t bytes);
ns_error_t Seek(uint64_t position);
ns_error_t Tell(uint64_t *position);
ns_error_t PeekByte(uint8_t *byte);
ns_error_t Sync();
ns_error_t Truncate();
bool WaitForRead(uint64_t size);
void ProcessMessage(nu::message_node_t *message);
void Wait(unsigned int milliseconds);
ProgressiveDownload download;
ProgressTracker progress_tracker;
FILE *progressive_file_read;
bool end_of_file;
bool connected;
int error_code;
nu::MessageLoop message_loop;
bool closed;
bool need_seek; // if set to true, we need to fseek(position)
};
ProgressiveDownload::ProgressiveDownload(ProgressTracker &progress_tracker, NXFileObject_ProgressiveDownloader &parent) : progress_tracker(progress_tracker), parent(parent)
{
killswitch=0;
url=0;
temp_filename=0;
progressive_file_write=0;
http=0;
user_agent=0;
download_thread=0;
file_size=0;
}
ProgressiveDownload::~ProgressiveDownload()
{
if (download_thread)
{
Close();
NXThreadJoin(download_thread, 0);
}
// TODO: flush messages
if (progressive_file_write)
fclose(progressive_file_write);
NXURIRelease(temp_filename);
NXURIRelease(url);
if (http)
jnl_http_release(http);
free(user_agent);
}
void ProgressiveDownload::Close()
{
nu::message_node_t *message = message_loop.AllocateMessage();
message->message = MESSAGE_KILL;
message_loop.PostMessage(message);
}
void ProgressiveDownload::Seek(uint64_t start, uint64_t end)
{
seek_message_t *message = (seek_message_t *)message_loop.AllocateMessage();
message->message = MESSAGE_SEEK;
message->start = start;
message->end = end;
message_loop.PostMessage(message);
}
ns_error_t ProgressiveDownload::Initialize(nx_uri_t url, jnl_http_t http, const char *user_agent, nx_uri_t temp_filename)
{
this->url = NXURIRetain(url);
this->temp_filename = NXURIRetain(temp_filename);
if (user_agent)
this->user_agent = strdup(user_agent);
this->http = jnl_http_retain(http);
progressive_file_write = NXFile_fopen(temp_filename, nx_file_FILE_readwrite_binary);
if (progressive_file_write == 0)
return NErr_FailedCreate;
return NXThreadCreate(&download_thread, _ProgressiveThread, this);
}
void ProgressiveDownload::ProcessMessage(nu::message_node_t *message)
{
switch(message->message)
{
case MESSAGE_KILL:
killswitch=1;
break;
case MESSAGE_SEEK:
{
seek_message_t *seek_message = (seek_message_t *)message;
char buffer[HTTP_BUFFER_SIZE] = {0};
/* empty out the jnetlib buffer. that might let us be able to avoid this seek */
DoRead(buffer, sizeof(buffer));
uint64_t new_start, new_end;
if (!progress_tracker.Valid(seek_message->start, seek_message->end) /* double check that we actually need to seek */
&& !progress_tracker.Seek(seek_message->start, seek_message->end, &new_start, &new_end))
{
int ret = SetupConnection(new_start, new_end);
if (ret == NErr_Success)
ret = Connect();
if (ret != NErr_Success)
{
parent.OnError(ret);
killswitch=1;
break;
}
_fseeki64(progressive_file_write, new_start, SEEK_SET);
}
else
parent.OnConnected();
}
break;
}
message_loop.FreeMessage(message);
}
int ProgressiveDownload::Wait(int milliseconds)
{
for (;;)
{
if (killswitch)
return 1;
nu::message_node_t *message = message_loop.PeekMessage(milliseconds);
if (message)
ProcessMessage(message);
else
break;
}
nu::message_node_t *message = message_loop.PeekMessage(milliseconds);
if (message)
ProcessMessage(message);
return killswitch;
}
ns_error_t ProgressiveDownload::SetupConnection(uint64_t start_position, uint64_t end_position)
{
if (!http)
http = jnl_http_create(HTTP_BUFFER_SIZE, 0);
if (!http)
return NErr_FailedCreate;
jnl_http_reset_headers(http);
if (user_agent)
jnl_http_addheadervalue(http, "User-Agent", user_agent);
if (start_position && start_position != (uint64_t)-1)
{
if (end_position == (uint64_t)-1)
{
char temp[128] = {0};
sprintf(temp, "Range: bytes=%llu-", start_position);
jnl_http_addheader(http, temp);
}
else
{
char temp[128] = {0};
sprintf(temp, "Range: bytes=%llu-%llu", start_position, end_position);
jnl_http_addheader(http, temp);
}
}
jnl_http_addheader(http, "Connection: Close"); // TODO: change if we ever want a persistent connection and downloading in chunks
jnl_http_connect(http, AutoCharUTF8(url), 1, "GET");
return NErr_Success;
}
int ProgressiveDownload::Connect()
{
// TODO: configurable timeout
/* wait for connection */
#ifdef _DEBUG
const int timeout = 15000;
#else
const int timeout = 15;
#endif
time_t start_time = time(0);
int http_status = jnl_http_get_status(http);
while (http_status == HTTPGET_STATUS_CONNECTING || http_status == HTTPGET_STATUS_READING_HEADERS)
{
if (Wait(55) != 0)
return NErr_Interrupted;
int ret = jnl_http_run(http);
if (ret == HTTPGET_RUN_ERROR)
return NErr_ConnectionFailed;
if (start_time + timeout < time(0))
return NErr_TimedOut;
http_status = jnl_http_get_status(http);
}
if (http_status == HTTPGET_STATUS_ERROR)
{
switch(jnl_http_getreplycode(http))
{
case 400:
return NErr_BadRequest;
case 401:
// TODO: deal with this specially
return NErr_Unauthorized;
case 403:
// TODO: deal with this specially?
return NErr_Forbidden;
case 404:
return NErr_NotFound;
case 405:
return NErr_BadMethod;
case 406:
return NErr_NotAcceptable;
case 407:
// TODO: deal with this specially
return NErr_ProxyAuthenticationRequired;
case 408:
return NErr_RequestTimeout;
case 409:
return NErr_Conflict;
case 410:
return NErr_Gone;
case 500:
return NErr_InternalServerError;
case 503:
return NErr_ServiceUnavailable;
default:
return NErr_ConnectionFailed;
}
}
else
{
if (!file_size)
{
// TODO: check range header for actual size
file_size = jnl_http_content_length(http);
parent.OnFileSize(file_size);
}
parent.OnConnected();
return NErr_Success;
}
}
void ProgressiveDownload::Internal_Write(const void *data, size_t data_len)
{
size_t bytes_written = fwrite(data, 1, data_len, progressive_file_write);
fflush(progressive_file_write);
progress_tracker.Write(bytes_written);
}
int ProgressiveDownload::DoRead(void *buffer, size_t bufferlen)
{
int ret = jnl_http_run(http);
size_t bytes_received;
do
{
ret = jnl_http_run(http);
bytes_received = jnl_http_get_bytes(http, buffer, bufferlen);
if (bytes_received)
{
Internal_Write(buffer, bytes_received);
}
/* TODO: benski> should we limit the number of times through this loop?
I'm worried that if data comes in fast enough we might get stuck in this for a long time */
} while (bytes_received == bufferlen);
return ret;
}
nx_thread_return_t ProgressiveDownload::ProgressiveThread()
{
ns_error_t ret;
if (!http)
{
ret = SetupConnection(0, (uint64_t)-1);
if (ret != NErr_Success)
{
parent.OnError(ret);
parent.OnClosed();
return 0;
}
}
ret = Connect();
if (ret != NErr_Success)
{
parent.OnError(ret);
}
else
{
for (;;)
{
if (Wait(10) == 1)
break; // killed!
char buffer[HTTP_BUFFER_SIZE] = {0};
int ret = DoRead(buffer, sizeof(buffer));
if (ret == -1)
break;
else if (ret == HTTPGET_RUN_CONNECTION_CLOSED)
{
if (jnl_http_bytes_available(http) == 0)
{
if (progress_tracker.Valid(0, file_size))
{
// file is completely downloaded. let's gtfo
fclose(progressive_file_write);
progressive_file_write=0;
break;
}
// if we're not completely full then we need to sit around for a potential MESSAGE_SEEK
//while (Wait(100) == 0)
{
// nop
}
}
}
}
}
parent.OnClosed();
return 0;
}
/* ------------------ */
NXFileObject_ProgressiveDownloader::NXFileObject_ProgressiveDownloader() : download(progress_tracker, *this)
{
progressive_file_read=0;
end_of_file=false;
connected=false;
error_code=NErr_Success;
closed = false;
need_seek=false;
position=0;
}
NXFileObject_ProgressiveDownloader::~NXFileObject_ProgressiveDownloader()
{
download.Close();
while (!closed)
Wait(10);
if (progressive_file_read)
fclose(progressive_file_read);
}
void NXFileObject_ProgressiveDownloader::OnConnected()
{
nu::message_node_t *message = message_loop.AllocateMessage();
message->message = MESSAGE_CONNECTED;
message_loop.PostMessage(message);
}
void NXFileObject_ProgressiveDownloader::OnError(int error_code)
{
error_message_t *message = (error_message_t *)message_loop.AllocateMessage();
message->message = MESSAGE_ERROR;
message->error_code = error_code;
message_loop.PostMessage(message);
}
void NXFileObject_ProgressiveDownloader::OnFileSize(uint64_t size)
{
size_message_t *message = (size_message_t *)message_loop.AllocateMessage();
message->message = MESSAGE_SIZE;
message->size = size;
message_loop.PostMessage(message);
}
void NXFileObject_ProgressiveDownloader::OnClosed()
{
nu::message_node_t *message = message_loop.AllocateMessage();
message->message = MESSAGE_CLOSED;
message_loop.PostMessage(message);
}
ns_error_t NXFileObject_ProgressiveDownloader::Initialize(nx_uri_t uri, jnl_http_t http, const char *user_agent)
{
ReferenceCountedNXURI temp_uri;
NXURICreateTemp(&temp_uri);
ns_error_t ret = download.Initialize(uri, http, user_agent, temp_uri);
if (ret != NErr_Success)
{
closed=true;
return ret;
}
progressive_file_read = NXFile_fopen(temp_uri, nx_file_FILE_read_binary);
for (;;)
{
Wait(10);
if (error_code != NErr_Success)
return error_code;
if (connected)
break;
}
return NErr_Success;
}
void NXFileObject_ProgressiveDownloader::ProcessMessage(nu::message_node_t *message)
{
switch(message->message)
{
case MESSAGE_ERROR:
{
error_message_t *seek_message = (error_message_t *)message;
error_code = seek_message->error_code;
}
break;
case MESSAGE_CONNECTED:
connected = true;
break;
case MESSAGE_SIZE:
{
size_message_t *seek_message = (size_message_t *)message;
region.end = seek_message->size;
}
break;
case MESSAGE_CLOSED:
closed=true;
break;
}
message_loop.FreeMessage(message);
}
void NXFileObject_ProgressiveDownloader::Wait(unsigned int milliseconds)
{
for (;;)
{
nu::message_node_t *message = message_loop.PeekMessage(milliseconds);
if (message)
ProcessMessage(message);
else
break;
}
nu::message_node_t *message = message_loop.PeekMessage(milliseconds);
if (message)
ProcessMessage(message);
}
bool NXFileObject_ProgressiveDownloader::WaitForRead(uint64_t size)
{
if (progress_tracker.Valid(position, position+size))
return true;
if (need_seek)
{
// give it just a little bit of time to avoid constant reseeks when the download thread is just barely keeping up
Wait(10);
if (progress_tracker.Valid(position, position+size))
return true;
connected=false;
error_code=NErr_Success;
download.Seek(position, (uint64_t)position+size);
for (;;)
{
Wait(10);
if (error_code != NErr_Success)
return false;
if (connected)
break;
}
}
while (!progress_tracker.Valid(position, position+size))
{
Wait(10);
}
return true;
}
ns_error_t NXFileObject_ProgressiveDownloader::Read(void *buffer, size_t bytes_requested, size_t *bytes_read)
{
if (end_of_file || position >= (region.end - region.start))
return NErr_EndOfFile;
// don't allow a read past the end of the file as this will confuse progress_tracker (which doesn't know/care about the file length)
if ((position + bytes_requested) > region.end)
bytes_requested = (size_t)(region.end - position);
if (WaitForRead((uint64_t)bytes_requested) == false)
{
*bytes_read = 0;
return error_code;
}
if (need_seek)
{
_fseeki64(progressive_file_read, position, SEEK_SET);
need_seek=false;
}
/* TODO: benski> if r < bytes_requested, then we need to flush the buffer.
on windows, we can use fflush(progressive_file_read)
on other platforms it's not guaranteed! */
size_t r = fread(buffer, 1, bytes_requested, progressive_file_read);
this->position += r;
*bytes_read = r;
return NErr_Success;
}
ns_error_t NXFileObject_ProgressiveDownloader::Seek(uint64_t new_position)
{
if (new_position >= (region.end - region.start))
{
this->position = region.end - region.start;
end_of_file=true;
}
else
{
if (new_position == position)
return NErr_Success;
position = new_position;
need_seek=true;
end_of_file=false;
}
return NErr_Success;
}
ns_error_t NXFileObject_ProgressiveDownloader::Tell(uint64_t *position)
{
if (end_of_file)
*position = region.end - region.start;
else
*position = this->position - region.start;
return NErr_Success;
}
ns_error_t NXFileObject_ProgressiveDownloader::PeekByte(uint8_t *byte)
{
if (position == region.end)
return NErr_EndOfFile;
// make sure we have enough room
if (WaitForRead((uint64_t)1) == false)
return error_code;
if (need_seek)
{
_fseeki64(progressive_file_read, position, SEEK_SET);
need_seek=false;
}
int read_byte = fgetc(progressive_file_read);
if (read_byte != EOF)
ungetc(read_byte, progressive_file_read);
else
{
/* TODO: benski> if we hit the point, then we actually need to flush the buffer.
on some platforms, fflush(progressive_file_read) will do that, but it's not guaranteed! */
return NErr_EndOfFile;
}
*byte = (uint8_t)read_byte;
return NErr_Success;
}
ns_error_t NXFileObject_ProgressiveDownloader::Sync()
{
return NErr_NotImplemented;
}
ns_error_t NXFileObject_ProgressiveDownloader::Truncate()
{
return NErr_NotImplemented;
}
ns_error_t NXFileObject_ProgressiveDownloader::Write(const void *buffer, size_t bytes)
{
return NErr_NotImplemented;
}
bool NXFileObject_ProgressiveDownloader::Available(uint64_t size, uint64_t *available)
{
uint64_t end = position+size;
if (end > region.end)
end = region.end;
if (position == region.end)
{
if (available)
*available=0;
return true;
}
return progress_tracker.Valid(position, end, available);
}
ns_error_t NXFileOpenProgressiveDownloader(nx_file_t *out_file, nx_uri_t filename, nx_file_FILE_flags_t flags, jnl_http_t http, const char *user_agent)
{
NXFileObject_ProgressiveDownloader *file_object = new (std::nothrow) NXFileObject_ProgressiveDownloader;
if (!file_object)
return NErr_OutOfMemory;
ns_error_t ret = file_object->Initialize(filename, http, user_agent);
if (ret != NErr_Success)
{
delete file_object;
return ret;
}
*out_file = (nx_file_t)file_object;
return NErr_Success;
}
ns_error_t NXFileProgressiveDownloaderAvailable(nx_file_t _f, uint64_t size, uint64_t *available)
{
if (!_f)
return NErr_BadParameter;
NXFileObject_ProgressiveDownloader *f = (NXFileObject_ProgressiveDownloader *)_f;
if (f->Available(size, available))
return NErr_True;
else
return NErr_False;
}

View File

@ -0,0 +1,209 @@
#include "NXFileObject.h"
#include <new>
#include "minizip/unzip.h"
#include <nx/nxfile.h>
#include <assert.h>
class NXZipFile : NXFileObject
{
public:
NXZipFile(unzFile zip_file);
~NXZipFile();
/* NXFileObject implementation */
ns_error_t Read(void *buffer, size_t bytes_requested, size_t *bytes_read);
ns_error_t Write(const void *buffer, size_t bytes);
ns_error_t Seek(uint64_t position);
ns_error_t Tell(uint64_t *position);
ns_error_t PeekByte(uint8_t *byte);
ns_error_t Sync();
ns_error_t Truncate();
// TODO(benski) implementation EOF
// TODO(benski) implement region locking
private:
unzFile zip_file;
};
NXZipFile::NXZipFile(unzFile zip_file) : zip_file(zip_file)
{
}
NXZipFile::~NXZipFile()
{
if (zip_file) {
unzCloseCurrentFile(zip_file);
unzClose(zip_file);
zip_file=0;
}
}
/* NXFileObject implementation */
ns_error_t NXZipFile::Read(void *buffer, size_t bytes_requested, size_t *bytes_read)
{
int zret = unzReadCurrentFile(zip_file, buffer, (unsigned int)bytes_requested);
if (zret == 0) {
if (bytes_read) {
*bytes_read = 0;
}
return NErr_EndOfFile;
} else if (zret > 0) {
if (bytes_read) {
*bytes_read = (size_t)zret;
}
return NErr_Success;
} else {
if (bytes_read) {
*bytes_read = 0;
}
return NErr_Error;
}
}
ns_error_t NXZipFile::Write(const void *buffer, size_t bytes)
{
return NErr_NotImplemented;
}
ns_error_t NXZipFile::Seek(uint64_t position)
{
// TODO(benski) error check)
unzSetOffset64(zip_file, position);
return NErr_Success;
}
ns_error_t NXZipFile::Tell(uint64_t *position)
{
*position = unzGetOffset64(zip_file);
return NErr_Success;
}
ns_error_t NXZipFile::PeekByte(uint8_t *byte)
{
return NErr_NotImplemented;
}
ns_error_t NXZipFile::Sync()
{
return NErr_NotImplemented;
}
ns_error_t NXZipFile::Truncate()
{
return NErr_NotImplemented;
}
static voidpf ZCALLBACK unzip_nxfile_open OF((voidpf opaque, const void* filename, int mode))
{
nx_file_t f;
if (NXFileOpenFile(&f, (nx_uri_t)filename, nx_file_FILE_read_binary) != NErr_Success) {
return 0;
}
return f;
}
static uLong ZCALLBACK unzip_nxfile_read OF((voidpf opaque, voidpf stream, void* buf, uLong size))
{
nx_file_t f = (nx_file_t)stream;
size_t bytes_read;
if (NXFileRead(f, buf, size, &bytes_read) != NErr_Success) {
return 0;
}
return (uLong)bytes_read;
}
static int ZCALLBACK unzip_nxfile_close OF((voidpf opaque, voidpf stream))
{
NXFileRelease((nx_file_t)stream);
return 0;
}
static ZPOS64_T ZCALLBACK unzip_nxfile_tell OF((voidpf opaque, voidpf stream))
{
nx_file_t f = (nx_file_t)stream;
uint64_t position;
if (NXFileTell(f, &position) == NErr_Success) {
return (int64_t)position;
} else {
return -1;
}
}
static long ZCALLBACK unzip_nxfile_seek OF((voidpf opaque, voidpf stream, ZPOS64_T offset, int whence))
{
nx_file_t f = (nx_file_t)stream;
uint64_t position;
if (whence == SEEK_SET) {
position = offset;
} else if (whence == SEEK_CUR) {
ns_error_t err = NXFileTell(f, &position);
if (err != NErr_Success) {
return -1;
}
position += offset;
} else if (whence == SEEK_END) {
uint64_t length;
NXFileLength(f, &length);
position = length + offset;
} else {
return -1;
}
ns_error_t err = NXFileSeek(f, position);
if (err == NErr_Success) {
return 0;
} else {
return -1;
}
}
#if 0
open64_file_func zopen64_file;
read_file_func zread_file;
write_file_func zwrite_file;
tell64_file_func ztell64_file;
seek64_file_func zseek64_file;
close_file_func zclose_file;
testerror_file_func zerror_file;
#endif
ns_error_t NXFileOpenZip(nx_file_t *out_file, nx_uri_t filename, nx_string_t extension_hint)
{
#if 0
typedef struct zlib_filefunc_def_s
{
open_file_func zopen_file;
read_file_func zread_file;
write_file_func zwrite_file;
tell_file_func ztell_file;
seek_file_func zseek_file;
close_file_func zclose_file;
testerror_file_func zerror_file;
voidpf opaque;
} zlib_filefunc_def;
#endif
zlib_filefunc64_def file_func = {0, };
file_func.zopen64_file = unzip_nxfile_open;
file_func.zread_file = unzip_nxfile_read;
file_func.ztell64_file = unzip_nxfile_tell;
file_func.zseek64_file = unzip_nxfile_seek;
file_func.zclose_file = unzip_nxfile_close;
unzFile zip_file = unzOpen2_64(filename, &file_func);
if (zip_file == NULL) {
return NErr_Error;
}
unzGoToFirstFile(zip_file);
// TODO(benski): look for filename with extension_hint as extension
// TODO(benski): search for anything with extension
unzOpenCurrentFile(zip_file);
NXZipFile *nx_zip_file = new (std::nothrow) NXZipFile(zip_file);
if (!nx_zip_file) {
unzCloseCurrentFile(zip_file);
unzClose(zip_file);
return NErr_OutOfMemory;
}
*out_file = (nx_file_t)nx_zip_file;
return NErr_Success;
}

View File

@ -0,0 +1,15 @@
#pragma once
#include "../../foundation/guid.h"
#ifdef NX_EXPORTS
#define NX_API __declspec(dllexport)
#else
#define NX_API __declspec(dllimport)
#endif
/* increment this any time that the NX API changes in a non-backwards-compatible way (preferably rarely) */
static const int nx_api_version = 1;
// {E7079A4B-BBB3-441F-ADCD-E0F1FE276EE3}
static const GUID nx_platform_guid =
{ 0xe7079a4b, 0xbbb3, 0x441f, { 0xad, 0xcd, 0xe0, 0xf1, 0xfe, 0x27, 0x6e, 0xe3 } };

View File

@ -0,0 +1,66 @@
#include "nxcondition.h"
#include "foundation/error.h"
int NXConditionInitialize(nx_condition_t condition)
{
if (condition == 0)
return NErr_NullPointer;
InitializeCriticalSection(&condition->mutex);
InitializeConditionVariable(&condition->condition);
return NErr_Success;
}
int NXConditionDestroy(nx_condition_t condition)
{
if (condition == 0)
return NErr_NullPointer;
DeleteCriticalSection(&condition->mutex);
return NErr_Success;
}
int NXConditionLock(nx_condition_t condition)
{
if (condition == 0)
return NErr_NullPointer;
EnterCriticalSection(&condition->mutex);
return NErr_Success;
}
int NXConditionUnlock(nx_condition_t condition)
{
if (condition == 0)
return NErr_NullPointer;
LeaveCriticalSection(&condition->mutex);
return NErr_Success;
}
int NXConditionWait(nx_condition_t condition)
{
if (condition == 0)
return NErr_NullPointer;
SleepConditionVariableCS(&condition->condition, &condition->mutex, INFINITE);
return NErr_Success;
}
int NXConditionTimedWait(nx_condition_t condition, unsigned int milliseconds)
{
if (condition == 0)
return NErr_NullPointer;
SleepConditionVariableCS(&condition->condition, &condition->mutex, milliseconds);
return NErr_Success;
}
int NXConditionSignal(nx_condition_t condition)
{
if (condition == 0)
return NErr_NullPointer;
WakeConditionVariable(&condition->condition);
return NErr_Success;
}

View File

@ -0,0 +1,28 @@
#pragma once
#include "nx/nxapi.h"
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct nx_condition_struct_t
{
CONDITION_VARIABLE condition;
CRITICAL_SECTION mutex;
} nx_condition_value_t, *nx_condition_t;
NX_API int NXConditionInitialize(nx_condition_t condition);
NX_API int NXConditionDestroy(nx_condition_t condition);
NX_API int NXConditionLock(nx_condition_t condition);
NX_API int NXConditionUnlock(nx_condition_t condition);
NX_API int NXConditionWait(nx_condition_t condition);
NX_API int NXConditionTimedWait(nx_condition_t condition, unsigned int milliseconds);
NX_API int NXConditionSignal(nx_condition_t condition);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,276 @@
#include "nx/nxdata.h"
#include "foundation/atomics.h"
#include "foundation/error.h"
#include "nx/nxfile.h"
#include <sys/stat.h>
/* windows implementation */
struct nx_data_struct_t
{
volatile size_t ref_count;
nx_string_t mime_type;
nx_string_t description;
nx_uri_t source_uri;
nx_file_stat_t source_stats;
size_t len;
uint8_t data[1];
};
static size_t NXDataMallocSize(size_t bytes)
{
/* TODO: overflow check? */
const nx_data_t dummy=0;
size_t header = (size_t)&dummy->data[0] - (size_t)dummy;
return header + bytes;
}
nx_data_t NXDataRetain(nx_data_t data)
{
if (!data)
return 0;
nx_atomic_inc(&data->ref_count);
return data;
}
void NXDataRelease(nx_data_t data)
{
if (data)
{
if (nx_atomic_dec(&data->ref_count) == 0)
{
free(data->source_stats);
NXURIRelease(data->source_uri);
NXStringRelease(data->mime_type);
NXStringRelease(data->description);
free(data);
}
}
}
int NXDataCreate(nx_data_t *out_data, const void *bytes, size_t length)
{
void *new_bytes;
int ret = NXDataCreateWithSize(out_data, &new_bytes, length);
if (ret != NErr_Success)
return ret;
memcpy(new_bytes, bytes, length);
return NErr_Success;
}
int NXDataCreateWithSize(nx_data_t *out_data, void **bytes, size_t length)
{
nx_data_t data = 0;
size_t data_length = NXDataMallocSize(length);
data = (nx_data_t)malloc(data_length);
if (!data)
return NErr_OutOfMemory;
data->ref_count = 1;
data->len = length;
data->mime_type=0;
data->source_uri=0;
data->source_stats=0;
data->description=0;
if (bytes)
*bytes = data->data;
*out_data=data;
return NErr_Success;
}
int NXDataCreateEmpty(nx_data_t *out_data)
{
return NXDataCreateWithSize(out_data, 0, 0);
}
int NXDataCreateFromURI(nx_data_t *out_data, nx_uri_t filename)
{
nx_file_stat_s stat_buffer;
nx_data_t data;
size_t data_length;
size_t bytes_read;
uint64_t file_length;
void *bytes;
int ret;
int fd;
fd = NXFile_open(filename, nx_file_O_BINARY|nx_file_O_RDONLY);
if (fd == -1)
return NErr_FileNotFound;
ret = NXFile_fstat(fd, &stat_buffer);
if (ret != NErr_Success)
{
close(fd);
return ret;
}
file_length = stat_buffer.file_size;
if (file_length > SIZE_MAX)
{
close(fd);
return NErr_IntegerOverflow;
}
data_length = (size_t)file_length;
ret = NXDataCreateWithSize(&data, &bytes, data_length);
if (ret != NErr_Success)
{
close(fd);
return ret;
}
data->source_stats=(nx_file_stat_t)malloc(sizeof(nx_file_stat_s));
if (!data->source_stats)
{
close(fd);
NXDataRelease(data);
return NErr_OutOfMemory;
}
bytes_read = read(fd, bytes, (int)data_length);
close(fd);
if (bytes_read != data_length)
{
NXDataRelease(data);
return NErr_Error;
}
*data->source_stats=stat_buffer;
data->source_uri=NXURIRetain(filename);
*out_data = data;
return NErr_Success;
}
int NXDataGet(nx_data_t data, const void **bytes, size_t *length)
{
if (!data)
return NErr_BadParameter;
if (data->len == 0)
return NErr_Empty;
*bytes = data->data;
*length = data->len;
return NErr_Success;
}
size_t NXDataSize(nx_data_t data)
{
if (!data)
return 0;
return data->len;
}
int NXDataSetMIME(nx_data_t data, nx_string_t mime_type)
{
nx_string_t old;
if (!data)
return NErr_BadParameter;
old = data->mime_type;
data->mime_type = NXStringRetain(mime_type);
NXStringRelease(old);
return NErr_Success;
}
int NXDataSetDescription(nx_data_t data, nx_string_t description)
{
nx_string_t old;
if (!data)
return NErr_BadParameter;
old = data->description;
data->description = NXStringRetain(description);
NXStringRelease(old);
return NErr_Success;
}
int NXDataSetSourceURI(nx_data_t data, nx_uri_t source_uri)
{
nx_uri_t old;
if (!data)
return NErr_BadParameter;
old = data->source_uri;
data->source_uri = NXURIRetain(source_uri);
NXURIRelease(old);
return NErr_Success;
}
int NXDataSetSourceStat(nx_data_t data, nx_file_stat_t source_stats)
{
nx_file_stat_t new_stats;
if (!data)
return NErr_BadParameter;
if (source_stats)
{
new_stats=(nx_file_stat_t)malloc(sizeof(nx_file_stat_s));
if (!new_stats)
return NErr_OutOfMemory;
*new_stats = *source_stats;
free(data->source_stats);
data->source_stats=new_stats;
}
else
{
free(data->source_stats);
data->source_stats=0;
}
return NErr_Success;
}
int NXDataGetMIME(nx_data_t data, nx_string_t *mime_type)
{
if (!data)
return NErr_BadParameter;
if (!data->mime_type)
return NErr_Empty;
*mime_type = NXStringRetain(data->mime_type);
return NErr_Success;
}
int NXDataGetDescription(nx_data_t data, nx_string_t *description)
{
if (!data)
return NErr_BadParameter;
if (!data->description)
return NErr_Empty;
*description = NXStringRetain(data->description);
return NErr_Success;
}
int NXDataGetSourceURI(nx_data_t data, nx_uri_t *source_uri)
{
if (!data)
return NErr_BadParameter;
if (!data->source_uri)
return NErr_Empty;
*source_uri = NXURIRetain(data->source_uri);
return NErr_Success;
}
int NXDataGetSourceStat(nx_data_t data, nx_file_stat_t *source_stats)
{
if (!data)
return NErr_BadParameter;
if (!data->source_stats)
return NErr_Empty;
*source_stats = data->source_stats;
return NErr_Success;
}

View File

@ -0,0 +1,42 @@
#pragma once
#include "../../foundation/types.h"
#include "../../nx/nxuri.h"
#include "../../nx/nxapi.h"
#include "../../nx/nxfile.h"
#include "../../nx/nxstring.h"
/* windows implementation */
#ifdef __cplusplus
extern "C" {
#endif
typedef struct nx_data_struct_t *nx_data_t;
NX_API nx_data_t NXDataRetain(nx_data_t data);
NX_API void NXDataRelease(nx_data_t data);
NX_API int NXDataCreate(nx_data_t *data, const void *bytes, size_t length);
NX_API int NXDataCreateWithSize(nx_data_t *data, void **bytes, size_t length);
/* creates an empty data object. useful if you need to store MIME, source URI, etc. without having actual data */
NX_API int NXDataCreateEmpty(nx_data_t *data);
/* you can pass file_stats==NULL if you don't care about the file stats */
NX_API int NXDataCreateFromURI(nx_data_t *data, nx_uri_t filename);
NX_API size_t NXDataSize(nx_data_t data);
NX_API int NXDataGet(nx_data_t data, const void **bytes, size_t *length);
/* You can _only_ call these on your own nx_data_t object _before_ you give it to anyone else! */
NX_API int NXDataSetMIME(nx_data_t data, nx_string_t mime_type);
NX_API int NXDataSetDescription(nx_data_t data, nx_string_t description);
NX_API int NXDataSetSourceURI(nx_data_t data, nx_uri_t source_uri);
NX_API int NXDataSetSourceStat(nx_data_t data, nx_file_stat_t source_stats);
/* you need to call NXStringRelease on what you get back (if the function succeeded) */
NX_API int NXDataGetMIME(nx_data_t data, nx_string_t *mime_type);
NX_API int NXDataGetDescription(nx_data_t data, nx_string_t *description);
NX_API int NXDataGetSourceURI(nx_data_t data, nx_uri_t *source_uri);
NX_API int NXDataGetSourceStat(nx_data_t data, nx_file_stat_t *source_stats);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,68 @@
#include "nxfile.h"
#include "foundation/error.h"
#include <sys/stat.h>
ns_error_t NXFile_move(nx_uri_t destination, nx_uri_t source)
{
if (!ReplaceFile(destination->string, source->string, 0, 0, 0, 0))
{
if (!MoveFile(source->string, destination->string))
{
if (!CopyFile(source->string, destination->string, FALSE))
{
return NErr_Error;
}
DeleteFile(source->string);
}
}
return NErr_Success;
}
ns_error_t NXFile_unlink(nx_uri_t filename)
{
if (DeleteFile(filename->string))
return NErr_Success;
else
return NErr_Error;
}
ns_error_t NXFile_stat(nx_uri_t filename, nx_file_stat_t file_stats)
{
struct __stat64 buffer;
if (_wstat64(filename->string, &buffer) == 0)
{
file_stats->access_time = buffer.st_atime;
file_stats->creation_time = buffer.st_ctime;
file_stats->modified_time = buffer.st_mtime;
file_stats->file_size = buffer.st_size;
return NErr_Success;
}
else
return NErr_Error;
}
ns_error_t NXFile_statFILE(FILE *f, nx_file_stat_t file_stats)
{
int fd = _fileno(f);
if (fd == -1)
return NErr_Error;
return NXFile_fstat(fd, file_stats);
}
ns_error_t NXFile_fstat(int file_descriptor, nx_file_stat_t file_stats)
{
struct __stat64 buffer;
if (_fstat64(file_descriptor, &buffer) == 0)
{
file_stats->access_time = buffer.st_atime;
file_stats->creation_time = buffer.st_ctime;
file_stats->modified_time = buffer.st_mtime;
file_stats->file_size = buffer.st_size;
return NErr_Success;
}
else
return NErr_Error;
}

View File

@ -0,0 +1,116 @@
#pragma once
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include "../../nx/nxapi.h"
#include <stdio.h> // for FILE
#include "../../nx/nxuri.h"
#include <io.h>
#include <fcntl.h>
#include "../../nx/nxtime.h"
#include "../../foundation/error.h"
#include "../../jnetlib/jnetlib.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct nx_file_stat_s
{
nx_time_unix_64_t creation_time;
nx_time_unix_64_t access_time;
nx_time_unix_64_t modified_time;
uint64_t file_size;
} nx_file_stat_s, *nx_file_stat_t;
typedef enum
{
nx_file_FILE_none = 0,
nx_file_FILE_binary = (1 << 0),
nx_file_FILE_read_text= (1 << 1),
nx_file_FILE_read_binary=nx_file_FILE_read_text|nx_file_FILE_binary,
nx_file_FILE_write_text=(1 << 2),
nx_file_FILE_write_binary=nx_file_FILE_write_text|nx_file_FILE_binary,
nx_file_FILE_update_text=(1 << 3),
nx_file_FILE_update_binary=nx_file_FILE_update_text|nx_file_FILE_binary,
nx_file_FILE_readwrite_text=(1 << 4),
nx_file_FILE_readwrite_binary=nx_file_FILE_readwrite_text|nx_file_FILE_binary,
nx_file_FILE_writable_mask = nx_file_FILE_write_text|nx_file_FILE_update_text|nx_file_FILE_readwrite_text,
} nx_file_FILE_flags_t;
static const int nx_file_O_BINARY=_O_BINARY;
static const int nx_file_O_WRONLY=_O_WRONLY;
static const int nx_file_O_RDONLY=_O_RDONLY;
static FILE *NXFile_fopen(nx_uri_t filename, nx_file_FILE_flags_t flags)
{
if (filename)
{
if (flags == nx_file_FILE_read_binary)
{
return _wfopen(filename->string, L"rb");
}
else if (flags == nx_file_FILE_write_binary)
{
return _wfopen(filename->string, L"wb");
}
else if (flags == nx_file_FILE_update_binary)
{
return _wfopen(filename->string, L"r+b");
}
else if (flags == nx_file_FILE_readwrite_binary)
{
return _wfopen(filename->string, L"w+b");
}
}
return 0;
}
/* returns a file descriptor */
static int NXFile_open(nx_uri_t filename, int flags)
{
return _wopen(filename->string, flags);
}
NX_API ns_error_t NXFile_move(nx_uri_t destination, nx_uri_t source);
NX_API ns_error_t NXFile_unlink(nx_uri_t filename);
NX_API ns_error_t NXFile_stat(nx_uri_t filename, nx_file_stat_t file_stats);
NX_API ns_error_t NXFile_statFILE(FILE *f, nx_file_stat_t file_stats);
NX_API ns_error_t NXFile_fstat(int file_descriptor, nx_file_stat_t file_stats);
/* --------------------------------------------------------------------------- */
typedef struct nx_file_s { size_t dummy; } *nx_file_t;
NX_API ns_error_t NXFileOpenFile(nx_file_t *out_file, nx_uri_t filename, nx_file_FILE_flags_t flags);
NX_API ns_error_t NXFileOpenProgressiveDownloader(nx_file_t *out_file, nx_uri_t filename, nx_file_FILE_flags_t flags, jnl_http_t http, const char *user_agent);
NX_API ns_error_t NXFileOpenZip(nx_file_t *out_file, nx_uri_t filename, nx_string_t extension_hint);
NX_API nx_file_t NXFileRetain(nx_file_t f);
NX_API void NXFileRelease(nx_file_t f);
/* the implementation of this function will only return NErr_EndOfFile if 0 bytes were read.
when *bytes_read < bytes_requested, it's likely that the file is at the end, but it will still return NErr_Success
until the next call */
NX_API ns_error_t NXFileRead(nx_file_t f, void *buffer, size_t bytes_requested, size_t *bytes_read);
NX_API ns_error_t NXFileWrite(nx_file_t f, const void *buffer, size_t bytes);
NX_API ns_error_t NXFileSeek(nx_file_t f, uint64_t position);
NX_API ns_error_t NXFileTell(nx_file_t f, uint64_t *position);
NX_API ns_error_t NXFileLockRegion(nx_file_t _f, uint64_t start_position, uint64_t end_position);
NX_API ns_error_t NXFileUnlockRegion(nx_file_t _f);
/* file_stats does _not_ take into account the current region */
NX_API ns_error_t NXFileStat(nx_file_t f, nx_file_stat_t file_stats);
/* returns the length of the file given the current region */
NX_API ns_error_t NXFileLength(nx_file_t f, uint64_t *length);
/* returns NErr_True, NErr_False, or possibly some error */
NX_API ns_error_t NXFileEndOfFile(nx_file_t f);
/* this exists as a one-off for nsmp4. hopefully we can get rid of it */
NX_API ns_error_t NXFilePeekByte(nx_file_t f, uint8_t *byte);
NX_API ns_error_t NXFileSync(nx_file_t f);
NX_API ns_error_t NXFileTruncate(nx_file_t f);
/* only valid for Progressive Downloader objects */
NX_API ns_error_t NXFileProgressiveDownloaderAvailable(nx_file_t f, uint64_t size, uint64_t *available);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,34 @@
#include "nximage.h"
static HANDLE image_heap;
void NXImageSetHeap(HANDLE _image_heap)
{
if (!image_heap)
image_heap = _image_heap;
}
static size_t NXImageMallocSize(size_t bytes)
{
return sizeof(nx_image_s) + bytes - sizeof(ARGB32);
}
nx_image_t NXImageMalloc(uint32_t width, uint32_t height)
{
size_t bytes;
nx_image_t img;
bytes = width*height*4; // TODO: overflow check
img = (nx_image_t)malloc(NXImageMallocSize(bytes));
img->ref_count = 1;
img->len = bytes;
img->width = width;
img->height = height;
return img;
}
nx_image_t NXImageRetain(nx_image_t image)
{
image->ref_count++;
return image;
}

View File

@ -0,0 +1,24 @@
#pragma once
#include "foundation/types.h"
#include "nxapi.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct nx_image
{
size_t ref_count;
uint32_t width;
uint32_t height;
size_t len;
ARGB32 image[1];
} nx_image_s, *nx_image_t;
NX_API void NXImageSetHeap(HANDLE image_heap);
NX_API nx_image_t NXImageMalloc(uint32_t width, uint32_t height);
NX_API nx_image_t NXImageRetain(nx_image_t image);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,19 @@
#include "nxlog.h"
#include <stdio.h>
#include <stdarg.h>
//#include <windows.h>
static char *nx_log_tag = "libreplicant";
#define MAX_FMT_SIZE 512
void NXLog(int priority, char *fmt, ...){
char formatted_string[MAX_FMT_SIZE];
va_list argptr;
va_start(argptr,fmt);
vsnprintf(formatted_string, MAX_FMT_SIZE, fmt, argptr);
va_end(argptr);
// OutputDebugString(formatted_string);
}

View File

@ -0,0 +1,13 @@
#pragma once
#include "foundation/types.h"
#include "nx/nxapi.h"
#ifdef __cplusplus
extern "C" {
#endif
NX_API void NXLog(int priority, char *fmt, ...);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,40 @@
#include "nxmutablestring.h"
#include "foundation/error.h"
/* currently this is closely coupled with the nx_string_t implementation. beware! */
extern HANDLE string_heap;
void NXMutableStringDestroy(nx_mutable_string_t mutable_string)
{
if (mutable_string)
{
if (mutable_string->nx_string_data)
NXStringRelease(mutable_string->nx_string_data);
HeapFree(string_heap, 0, mutable_string);
}
}
nx_mutable_string_t NXMutableStringCreateFromXML(const nsxml_char_t *characters, size_t num_characters)
{
nx_mutable_string_t mutable_string = (nx_mutable_string_t)HeapAlloc(string_heap, 0, sizeof(nx_mutable_string_struct_t));
NXStringCreateWithBytes(&mutable_string->nx_string_data, characters, num_characters*2, nx_charset_utf16le);
mutable_string->allocation_length = num_characters;
return mutable_string;
}
int NXMutableStringGrowFromXML(nx_mutable_string_t mutable_string, const nsxml_char_t *characters, size_t num_characters)
{
if (mutable_string->nx_string_data->len + num_characters + 1 > mutable_string->allocation_length)
{
nx_string_t new_string = NXStringRealloc(mutable_string->nx_string_data, mutable_string->nx_string_data->len + num_characters + 1);
if (!new_string)
return NErr_OutOfMemory;
mutable_string->nx_string_data = new_string;
mutable_string->allocation_length = mutable_string->nx_string_data->len + num_characters + 1;
}
memcpy(mutable_string->nx_string_data->string + mutable_string->nx_string_data->len, characters, num_characters*sizeof(wchar_t));
mutable_string->nx_string_data->len += num_characters;
mutable_string->nx_string_data->string[mutable_string->nx_string_data->len]=0; /* well null terminate */
return NErr_Success;
}

View File

@ -0,0 +1,24 @@
#pragma once
#include "foundation/types.h"
#include "nx/nxstring.h"
#include "nx/nxapi.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct nx_mutable_string_struct_t
{
size_t allocation_length;
nx_string_t nx_string_data;
} nx_mutable_string_struct_t, *nx_mutable_string_t;
NX_API nx_mutable_string_t NXMutableStringCreateFromXML(const nsxml_char_t *characters, size_t num_characters);
NX_API void NXMutableStringDestroy(nx_mutable_string_t mutable_string);
NX_API int NXMutableStringGrowFromXML(nx_mutable_string_t mutable_string, const nsxml_char_t *characters, size_t num_characters);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,43 @@
#include "nxonce.h"
#include "foundation/error.h"
#if 0 && _WIN32_WINNT >= 0x600
void NXOnce(nx_once_t once, int (NX_ONCE_API *init_fn)(nx_once_t, void *, void **), void *param)
{
InitOnceExecuteOnce(once, (PINIT_ONCE_FN)init_fn, param, 0);
}
void NXOnceInit(nx_once_t once)
{
InitOnceInitialize(once);
}
#else
/* this ONLY works because of the strict(ish) memory ordering of the AMD64/x86 processors.
Don't use this implementation for a processor that has loose memory ordering restriction (e.g. ARM, PowerPC)
see http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf
*/
void NXOnce(nx_once_t once, int (NX_ONCE_API *init_fn)(nx_once_t, void *, void **), void *param)
{
if (once->status)
return;
EnterCriticalSection(&once->critical_section);
if (once->status)
{
LeaveCriticalSection(&once->critical_section);
return;
}
init_fn(once, param, 0);
// benski> not important for the x86, but on processors with weak memory-order on stores, once->status might set to 1 BEFORE all stores from init_fn complete!
once->status = 1;
LeaveCriticalSection(&once->critical_section);
}
void NXOnceInit(nx_once_t once)
{
once->status=0;
InitializeCriticalSection(&once->critical_section);
}
#endif

View File

@ -0,0 +1,35 @@
#pragma once
#include "nxapi.h"
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#ifdef __cplusplus
extern "C" {
#endif
#if 0 && _WIN32_WINNT >= 0x600
typedef INIT_ONCE nx_once_value_t;
typedef INIT_ONCE *nx_once_t;
#define NX_ONCE_INITIALIZE INIT_ONCE_STATIC_INIT
#define NX_ONCE_API CALLBACK
NX_API void NXOnce(nx_once_t once, int (NX_ONCE_API *init_fn)(nx_once_t, void *, void **), void *param);
NX_API void NXOnceInit(nx_once_t once);
#else
typedef struct nx_once_s
{
volatile int status;
CRITICAL_SECTION critical_section;
} nx_once_value_t, *nx_once_t;
#define NX_ONCE_API
NX_API void NXOnce(nx_once_t once, int (NX_ONCE_API *init_fn)(nx_once_t, void *, void **), void *);
NX_API void NXOnceInit(nx_once_t once);
#endif
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,113 @@
#include "nxpath.h"
#include <shlwapi.h>
#include "foundation/error.h"
static const wchar_t *FindExtension(nx_uri_t filename)
{
size_t position;
if (!filename || !filename->string || !filename->len)
return 0;
position=filename->len;
while (position--)
{
wchar_t c = filename->string[position];
if (c == '.')
return &filename->string[position+1];
if (c == '/' || c == '\\')
return 0;
}
return 0;
}
static const wchar_t *FindFilename(nx_uri_t filename)
{
size_t position;
if (!filename || !filename->string || !filename->len)
return 0;
position=filename->len;
while (position--)
{
wchar_t c = filename->string[position];
if (c == '/' || c == '\\')
return &filename->string[position+1];
}
return 0;
}
size_t NXPathMatchExtensionList(nx_uri_t filename, nx_string_t *extension_list, size_t num_extensions)
{
const wchar_t *ext = FindExtension(filename);
if (ext && *ext)
{
size_t i;
for (i=0;i<num_extensions;i++)
{
#if WINVER >= 0x0600
if (CompareStringOrdinal(ext, -1, extension_list[i]->string, -1, TRUE) == CSTR_EQUAL)
return i;
#else
if (CompareString(LOCALE_INVARIANT, NORM_IGNORECASE, ext, -1, extension_list[i]->string, -1) == CSTR_EQUAL)
return i;
#endif
}
}
return num_extensions;
}
int NXPathMatchExtension(nx_uri_t filename, nx_string_t extension)
{
const wchar_t *ext = FindExtension(filename);
if (ext && *ext)
{
#if WINVER >= 0x0600
if (CompareStringOrdinal(ext, -1, extension->string, -1, TRUE) == CSTR_EQUAL)
return NErr_Success;
#else
if (CompareString(LOCALE_INVARIANT, NORM_IGNORECASE, ext, -1, extension->string, -1) == CSTR_EQUAL)
return NErr_Success;
#endif
}
return NErr_False;
}
int NXPathProtocol(nx_uri_t filename, const char *protocol)
{
if (PathIsURL(filename->string))
{
wchar_t protocol_from_filename[100];
DWORD protocol_length=100;
if (UrlGetPart(filename->string, protocol_from_filename, &protocol_length, URL_PART_SCHEME, 0) == S_OK)
{
DWORD i;
for (i=0;i<protocol_length;i++)
{
if ((wchar_t)(protocol[i]) != protocol_from_filename[i])
return NErr_False;
}
return NErr_Success;
}
}
return NErr_False;
}
int NXPathIsURL(nx_uri_t filename)
{
if (PathIsURL(filename->string))
return NErr_True;
else
return NErr_False;
}
int NXPathIsRelative(nx_uri_t filename)
{
if (filename->len >= 3 && filename->string[1] == L':' && (filename->string[2] == L'\\' || filename->string[2] == L'/'))
return NErr_False;
if (filename->len >= 1 && (filename->string[2] == L'\\' || filename->string[2] == L'/'))
return NErr_False;
return NErr_True;
}

View File

@ -0,0 +1,26 @@
#pragma once
#include "foundation/types.h"
#include "nx/nxapi.h"
#include "nx/nxstring.h"
#include "nx/nxuri.h"
#ifdef __cplusplus
extern "C" {
#endif
// returns index into the extension list of a match extension
// returns >= num_extensions on failure
NX_API size_t NXPathMatchExtensionList(nx_uri_t filename, nx_string_t *extension_list, size_t num_extensions);
// return NErr_True / NErr_False
NX_API int NXPathMatchExtension(nx_uri_t filename, nx_string_t extension);
// return NErr_True / NErr_False
NX_API int NXPathProtocol(nx_uri_t filename, const char *protocol);
// return NErr_True / NErr_False
NX_API int NXPathIsURL(nx_uri_t filename);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,24 @@
#include "nxsemaphore.h"
#include "foundation/error.h"
int NXSemaphoreCreate(nx_semaphore_t *sem)
{
*sem = CreateSemaphore(0, 0, LONG_MAX, 0);
return NErr_Success;
}
int NXSemaphoreRelease(nx_semaphore_t sem)
{
ReleaseSemaphore(sem, 1, 0);
return NErr_Success;
}
int NXSemaphoreWait(nx_semaphore_t sem)
{
WaitForSingleObject(sem, INFINITE);
return NErr_Success;
}
void NXSemaphoreClose(nx_semaphore_t sem)
{
CloseHandle(sem);
}

View File

@ -0,0 +1,16 @@
#pragma once
#include <windows.h>
#include "nx/nxapi.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef HANDLE nx_semaphore_t;
NX_API int NXSemaphoreCreate(nx_semaphore_t *sem);
NX_API int NXSemaphoreRelease(nx_semaphore_t sem);
NX_API int NXSemaphoreWait(nx_semaphore_t sem);
NX_API void NXSemaphoreClose(nx_semaphore_t sem);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,14 @@
#include "nxsleep.h"
#include "foundation/error.h"
int NXSleep(unsigned int milliseconds)
{
Sleep(milliseconds);
return NErr_Success;
}
int NXSleepYield(void)
{
Sleep(0);
return NErr_Success;
}

View File

@ -0,0 +1,17 @@
#pragma once
#include "nx/nxapi.h"
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#ifdef __cplusplus
extern "C" {
#endif
NX_API int NXSleep(unsigned int milliseconds);
NX_API int NXSleepYield(void);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,756 @@
#include "nxstring.h"
#include "foundation/error.h"
#include <shlwapi.h>
#include "foundation/atomics.h"
#include <wchar.h>
#include <stdarg.h>
#include <stdio.h>
#include <assert.h>
#pragma comment(lib, "shlwapi.lib")
//#define NX_STRING_STRICT_HEAP
HANDLE string_heap = 0;
int NXStringSetHeap(HANDLE _string_heap)
{
if (!string_heap)
{
string_heap = _string_heap;
return NErr_Success;
}
else
{
return NErr_NoAction;
}
}
// don't include null terminator here
static size_t NXStringMallocSize(size_t characters)
{
/* TODO: overflow check? */
const nx_string_t dummy=NULL;
size_t header = (size_t)&dummy->string[0] - (size_t)dummy;
return header + (characters+1) * sizeof(wchar_t);
}
// don't include null terminator here
nx_string_t NXStringMalloc(size_t characters)
{
if (!string_heap)
{
string_heap = GetProcessHeap();
}
return NXStringMallocWithHeap(string_heap, characters);
}
nx_string_t NXStringRealloc(nx_string_t str, size_t characters)
{
nx_string_t new_str = (nx_string_t)HeapReAlloc(string_heap, 0, str, NXStringMallocSize(characters));
// on failure, kick back the original block (TODO need to review this)
if (!new_str)
{
return str;
}
return new_str;
}
nx_string_t NXStringMallocWithHeap(HANDLE heap, size_t characters)
{
#ifdef NX_STRING_STRICT_HEAP
nx_string_t str;
size_t string_size = NXStringMallocSize(characters);
size_t allocated_size = (string_size + 8191) & ~4095;
size_t offset = 4096 - (string_size & 4095);
size_t pages = allocated_size / 4096;
uint8_t *protect_start;
void *mem = VirtualAlloc(0, allocated_size, MEM_COMMIT, PAGE_READWRITE);
if (!mem)
return 0;
protect_start = (uint8_t *)mem + (pages-1)*4096;
VirtualProtect(protect_start, 4096, PAGE_NOACCESS, 0);
str = (nx_string_t)((uint8_t *)mem + offset);
str->ref_count = 1;
str->len = characters;
return str;
#else
nx_string_t str = (nx_string_t)HeapAlloc(heap, 0, NXStringMallocSize(characters));
if (str)
{
str->ref_count = 1;
str->len = characters;
}
return str;
#endif
}
int NXStringFree(HANDLE heap, nx_string_t str)
{
#ifdef NX_STRING_STRICT_HEAP
uint8_t *mem = (uint8_t *)((size_t)str & 4095);
VirtualProtect(mem, 4096, PAGE_NOACCESS, 0);
assert(_heapchk() == _HEAPOK);
return NErr_Success;
#else
if (HeapFree(heap, 0, str))
{
return NErr_Success;
}
else
{
return NErr_Error;
}
#endif
}
nx_string_t NXStringCreate(const wchar_t *str)
{
size_t size;
nx_string_t nxstr;
if (!str || (size_t)str <= 65536)
{
return 0;
}
size = wcslen(str);
nxstr = NXStringMalloc(size);
if (nxstr)
{
memcpy(nxstr->string, str, size*sizeof(wchar_t));
nxstr->string[size]=0;
}
return nxstr;
}
int NXStringCreateEmpty(nx_string_t *new_string)
{
nx_string_t nxstr = NXStringMalloc(0);
if (nxstr)
{
nxstr->string[0]=0;
*new_string = nxstr;
return NErr_Success;
}
else
{
return NErr_OutOfMemory;
}
}
nx_string_t NXStringCreateWithHeap(HANDLE heap, const wchar_t *str)
{
size_t size = wcslen(str);
nx_string_t nxstr = NXStringMallocWithHeap(heap, size);
if (nxstr)
{
memcpy(nxstr->string, str, size*sizeof(wchar_t));
nxstr->string[size]=0;
}
return nxstr;
}
nx_string_t NXStringCreateFromUTF8(const char *str)
{
nx_string_t nxstr;
size_t size = MultiByteToWideChar(CP_UTF8, 0, str, -1, 0,0);
if (!size)
{
return 0;
}
nxstr = NXStringMalloc(size-1);
if (nxstr)
{
if (!MultiByteToWideChar(CP_UTF8, 0, str, -1, nxstr->string, (int)size))
{
NXStringFree(string_heap, nxstr);
return 0;
}
}
return nxstr;
}
int NXStringCreateWithUTF8(nx_string_t *new_value, const char *str)
{
size_t size;
nx_string_t nxstr;
if (!str)
{
return NErr_Empty;
}
size = MultiByteToWideChar(CP_UTF8, 0, str, -1, 0,0);
if (!size)
{
return NErr_Error;
}
nxstr = NXStringMalloc(size-1);
if (!nxstr)
{
return NErr_OutOfMemory;
}
if (!MultiByteToWideChar(CP_UTF8, 0, str, -1, nxstr->string, (int)size))
{
NXStringFree(string_heap, nxstr);
return NErr_Error;
}
*new_value = nxstr;
return NErr_Success;
}
int NXStringCreateWithUTF16(nx_string_t *new_value, const wchar_t *str)
{
size_t size;
nx_string_t nxstr;
if (!str)
{
return NErr_Empty;
}
size = wcslen(str);
nxstr = NXStringMalloc(size);
if (!nxstr)
{
return NErr_OutOfMemory;
}
memcpy(nxstr->string, str, size*sizeof(wchar_t));
nxstr->string[size]=0;
*new_value = nxstr;
return NErr_Success;
}
int NXStringCreateWithCString(nx_string_t *new_value, const char *str, nx_charset_t charset)
{
nx_string_t nxstr;
size_t size = MultiByteToWideChar(charset, 0, str, -1, 0,0);
if (!size)
{
return NErr_Error;
}
nxstr = NXStringMalloc(size-1);
if (!nxstr)
{
return NErr_OutOfMemory;
}
if (!MultiByteToWideChar(charset, 0, str, -1, nxstr->string, (int)size))
{
NXStringFree(string_heap, nxstr);
return NErr_Error;
}
*new_value = nxstr;
return NErr_Success;
}
nx_string_t NXStringRetain(nx_string_t string)
{
if (!string)
{
return 0;
}
nx_atomic_inc(&string->ref_count);
return string;
}
void NXStringRelease(nx_string_t string)
{
if (string)
{
if (nx_atomic_dec(&string->ref_count) == 0)
{
NXStringFree(string_heap, string);
}
}
}
nx_string_t NXStringCreateFromPath(const wchar_t *folder, const wchar_t *filename)
{
nx_string_t pathstr = NXStringMalloc(MAX_PATH);
if (pathstr)
{
PathCombineW(pathstr->string, folder, filename);
pathstr->len = wcslen(pathstr->string);
}
return pathstr;
}
nx_string_t NXStringCreateFromUInt64(uint64_t value)
{
nx_string_t intstr = NXStringMalloc(21);
if (intstr)
{
_ui64tow(value, intstr->string, 10);
intstr->len = wcslen(intstr->string);
}
return intstr;
}
int NXStringCreateWithUInt64(nx_string_t *new_value, uint64_t value)
{
nx_string_t intstr = NXStringMalloc(21);
if (!intstr)
{
return NErr_OutOfMemory;
}
_ui64tow(value, intstr->string, 10);
intstr->len = wcslen(intstr->string);
*new_value = intstr;
return NErr_Success;
}
int NXStringCreateWithInt64(nx_string_t *new_value, int64_t value)
{
nx_string_t intstr = NXStringMalloc(21);
if (!intstr)
{
return NErr_OutOfMemory;
}
_i64tow(value, intstr->string, 10);
intstr->len = wcslen(intstr->string);
*new_value = intstr;
return NErr_Success;
}
int NXStringCreateWithBytes(nx_string_t *new_string, const void *data, size_t len, nx_charset_t charset)
{
nx_string_t nxstr;
if (!len)
{
return NXStringCreateEmpty(new_string);
}
if (charset == nx_charset_utf16le)
{
nxstr = NXStringMalloc(len/2);
if (nxstr)
{
memcpy(nxstr->string, data, len);
nxstr->string[len/2]=0;
nxstr->len = len/2;
*new_string = nxstr;
return NErr_Success;
}
else
{
return NErr_OutOfMemory;
}
}
else if (charset == nx_charset_utf16be)
{
nxstr = NXStringMalloc(len/2);
if (nxstr)
{
LCMapString(LOCALE_INVARIANT, LCMAP_BYTEREV, (LPCWSTR)data, (int)len/2, nxstr->string, (int)len/2);
nxstr->string[len/2]=0;
nxstr->len = len/2;
*new_string = nxstr;
return NErr_Success;
}
else
{
return NErr_OutOfMemory;
}
}
else
{
int size = MultiByteToWideChar((UINT)charset, 0, (const char *)data, (int)len, 0, 0);
if (!size)
{
return NErr_Error;
}
nxstr = NXStringMalloc(size);
if (nxstr)
{
if (!MultiByteToWideChar((UINT)charset, 0, (const char *)data, (int)len, nxstr->string, size))
{
NXStringFree(string_heap, nxstr);
return NErr_Error;
}
nxstr->string[size]=0;
nxstr->len = size;
*new_string = nxstr;
return NErr_Success;
}
else
{
return NErr_OutOfMemory;
}
}
}
size_t NXStringGetLength(nx_string_t string)
{
return (string ? string->len : 0);
}
/* --- Keyword (ASCII) comparison --- */
int NXStringKeywordCompareWithCString(nx_string_t string, const char *compare_to)
{
const wchar_t *src = string->string;
const char *dst = compare_to;
int ret = 0 ;
while( ! (ret = (int)((*src & ~0x20) - (*dst & ~0x20))) && *dst)
{
++src, ++dst;
}
if ( ret < 0 )
{
ret = -1 ;
}
else if ( ret > 0 )
{
ret = 1 ;
}
return( ret );
}
int NXStringKeywordCompare(nx_string_t string, nx_string_t compare_to)
{
const wchar_t *src = string->string;
const wchar_t *dst = compare_to->string;
int ret = 0 ;
while( ! (ret = (int)((*src & ~0x20) - (*dst & ~0x20))) && *dst)
{
++src, ++dst;
}
if ( ret < 0 )
{
ret = -1 ;
}
else if ( ret > 0 )
{
ret = 1 ;
}
return( ret );
}
int NXStringKeywordCaseCompare(nx_string_t string, nx_string_t compare_to)
{
const wchar_t *src = string->string;
const wchar_t *dst = compare_to->string;
int ret = 0 ;
while( ! (ret = (int)(*src - (wchar_t)*dst)) && *dst)
{
++src, ++dst;
}
if ( ret < 0 )
{
ret = -1 ;
}
else if ( ret > 0 )
{
ret = 1 ;
}
return( ret );
}
int NXStringCreateBasePathFromFilename(nx_string_t filename, nx_string_t *basepath)
{
nx_string_t nxstr;
size_t len = filename->len;
while (len && filename->string[len-1] != '\\' && filename->string[len-1] != '/')
{
len--;
}
if (!len)
{
return NErr_Empty;
}
nxstr = NXStringMalloc(len);
if (!nxstr)
{
return NErr_OutOfMemory;
}
memcpy(nxstr->string, filename->string, sizeof(wchar_t)*len);
nxstr->string[len]=0;
*basepath = nxstr;
return NErr_Success;
}
int NXStringGetCString(nx_string_t string, char *user_buffer, size_t user_buffer_length, const char **out_cstring, size_t *out_cstring_length)
{
size_t size;
/* TODO: error check this with large strings and small user_buffer_length sizes */
if (!string)
{
return NErr_NullPointer;
}
if (user_buffer_length == 0)
return NErr_Insufficient;
size = WideCharToMultiByte(CP_ACP, 0, string->string, (int)string->len, user_buffer, (int)user_buffer_length-1, NULL, NULL);
if (size == 0 && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
size = user_buffer_length-1;
user_buffer[size]=0;
*out_cstring = user_buffer;
*out_cstring_length = (size_t)size;
return NErr_Success;
}
int NXStringGetDoubleValue(nx_string_t string, double *value)
{
if (!string)
return NErr_NullPointer;
*value = wcstod(string->string, 0);
return NErr_Success;
}
int NXStringGetBytesSize(size_t *byte_count, nx_string_t string, nx_charset_t charset, int flags)
{
if (charset == nx_charset_utf16le)
{
if (flags & nx_string_get_bytes_size_null_terminate)
*byte_count = (string->len + 1)* sizeof(wchar_t);
else
*byte_count = string->len * sizeof(wchar_t);
return NErr_DirectPointer;
}
else
{
size_t size=0;
/*if (flags & nx_string_get_bytes_size_null_terminate)
size = WideCharToMultiByte(charset, 0, string->string, string->len, 0, 0, NULL, NULL);
else*/
size = WideCharToMultiByte(charset, 0, string->string, (int)string->len, 0, 0, NULL, NULL);
if (!size)
return NErr_Error;
if (flags & nx_string_get_bytes_size_null_terminate)
*byte_count = size+1;
else
*byte_count = size;
return NErr_Success;
}
}
int NXStringGetBytesDirect(const void **bytes, size_t *length, nx_string_t string, nx_charset_t charset, int flags)
{
if (charset == nx_charset_utf16le)
{
*bytes = string->string;
if (length)
{
if (flags & nx_string_get_bytes_size_null_terminate)
*length = (string->len+1) * sizeof(wchar_t); // TODO: overflow check
else
*length = string->len * sizeof(wchar_t); // TODO: overflow check
}
return NErr_Success;
}
else
{
return NErr_Error;
}
}
int NXStringGetBytes(size_t *bytes_copied, nx_string_t string, void *bytes, size_t length, nx_charset_t charset, int flags)
{
if (charset == nx_charset_utf16le)
{
length/=2;
if (flags & nx_string_get_bytes_size_null_terminate)
{
if (length == 0)
return NErr_Insufficient;
length--;
}
if (length > string->len)
length = string->len;
wmemcpy((wchar_t *)bytes, string->string, length);
if (flags & nx_string_get_bytes_size_null_terminate)
((wchar_t *)bytes)[length++]=0;
if (bytes_copied)
*bytes_copied = length * 2;
return NErr_Success;
}
else
{
size_t size=0;
if (flags & nx_string_get_bytes_size_null_terminate)
{
size = WideCharToMultiByte(charset, 0, string->string, (int)string->len, (LPSTR)bytes, (int)length-1, NULL, NULL);
((char *)bytes)[size]=0;
}
else
{
size = WideCharToMultiByte(charset, 0, string->string, (int)string->len, (LPSTR)bytes, (int)length, NULL, NULL);
}
if (!size)
{
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
{
if (flags & nx_string_get_bytes_size_null_terminate)
size = length-1;
else
size=length;
}
else
{
return NErr_Error;
}
}
if (bytes_copied)
{
if (flags & nx_string_get_bytes_size_null_terminate)
{
if (size)
*bytes_copied = size+1;
else
*bytes_copied = length+1;
}
else
{
if (size)
*bytes_copied = size;
else
*bytes_copied = length;
}
}
return NErr_Success;
}
}
int NXStringGetIntegerValue(nx_string_t string, int *value)
{
*value = wcstol(string->string, 0, 10);
return NErr_Success;
}
int NXStringGetGUIDValue(nx_string_t string, GUID *out_guid)
{
/* TODO: it'd be nice if this was a bit more flexible on input, e.g. no dashes vs dashes */
GUID guid = GUID_NULL;
size_t offset = 0;
int Data1, Data2, Data3;
int Data4[8] = {0};
for (;;)
{
if (string->string[offset] == '{')
{
offset++;
}
else if (string->string[offset] == ' ')
{
offset++;
}
else
{
break;
}
}
//{ 0x1b3ca60c, 0xda98, 0x4826, { 0xb4, 0xa9, 0xd7, 0x97, 0x48, 0xa5, 0xfd, 0x73 } };
swscanf( string->string, L"%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
&Data1, &Data2, &Data3, Data4 + 0, Data4 + 1,
Data4 + 2, Data4 + 3, Data4 + 4, Data4 + 5, Data4 + 6, Data4 + 7 );
// Cross assign all the values
guid.Data1 = Data1;
guid.Data2 = Data2;
guid.Data3 = Data3;
guid.Data4[0] = Data4[0];
guid.Data4[1] = Data4[1];
guid.Data4[2] = Data4[2];
guid.Data4[3] = Data4[3];
guid.Data4[4] = Data4[4];
guid.Data4[5] = Data4[5];
guid.Data4[6] = Data4[6];
guid.Data4[7] = Data4[7];
*out_guid = guid;
return NErr_Success;
}
nx_compare_result NXStringCompare(nx_string_t string1, nx_string_t string2, nx_compare_options options)
{
int compareFlags = 0;
if (0 != (nx_compare_case_insensitive & options))
{
compareFlags |= NORM_IGNORECASE;
}
return CompareString(LOCALE_USER_DEFAULT, compareFlags, string1->string, -1, string2->string, -1) - 2;
}
int NXStringCreateWithFormatting(nx_string_t *new_string, const char *format, ...)
{
size_t cch, ret;
char *temp = 0;
va_list v;
va_start(v, format);
cch = _vscprintf(format, v);
if (cch == -1)
{
return NErr_Error;
}
if (cch > 256)
{
temp = (char *)malloc(cch+1);
if (!temp)
{
return NErr_OutOfMemory;
}
vsprintf(temp, format, v);
ret = NXStringCreateWithUTF8(new_string, temp);
free(temp);
}
else
{
temp = (char *)_malloca(cch+1);
if (!temp)
{
return NErr_OutOfMemory;
}
vsprintf(temp, format, v);
ret = NXStringCreateWithUTF8(new_string, temp);
}
va_end(v);
return (int)ret;
}

View File

@ -0,0 +1,95 @@
#pragma once
#include "../../foundation/types.h"
#include <windows.h>
#include "../../nx/nxapi.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef enum
{
nx_charset_ascii = 20127,
nx_charset_latin1 = 28591,
nx_charset_system = CP_ACP,
nx_charset_utf8 = CP_UTF8,
nx_charset_utf16le = 1200,
nx_charset_utf16be = 1201,
} nx_charset_t;
typedef struct nx_string_struct_t
{
size_t ref_count;
size_t len;
wchar_t string[1]; // utf-16
} nx_string_struct_t, *nx_string_t;
enum
{
nx_compare_less_than = -1,
nx_compare_equal_to = 0,
nx_compare_greater_than = 1,
};
typedef int nx_compare_result;
enum
{
nx_compare_default = 0,
nx_compare_case_insensitive = ( 1 << 0),
};
typedef unsigned long nx_compare_options;
NX_API int NXStringSetHeap(HANDLE string_heap);
NX_API nx_string_t NXStringCreateFromPath(const wchar_t *folder, const wchar_t *filename);
NX_API nx_string_t NXStringCreate(const wchar_t *str);
NX_API nx_string_t NXStringCreateWithHeap(HANDLE heap, const wchar_t *str);
NX_API nx_string_t NXStringMallocWithHeap(HANDLE heap, size_t characters);
NX_API nx_string_t NXStringMalloc(size_t characters);
NX_API nx_string_t NXStringRealloc(nx_string_t, size_t characters);
NX_API nx_string_t NXStringRetain(nx_string_t string);
NX_API void NXStringRelease(nx_string_t string);
NX_API nx_string_t NXStringCreateFromUTF8(const char *str);
NX_API int NXStringCreateWithUTF8(nx_string_t *new_value, const char *str);
NX_API int NXStringCreateWithUTF16(nx_string_t *new_value, const wchar_t *str);
NX_API int NXStringCreateWithCString(nx_string_t *new_value, const char *str, nx_charset_t charset);
NX_API nx_string_t NXStringCreateFromUInt64(uint64_t value);
NX_API int NXStringCreateWithUInt64(nx_string_t *new_value, uint64_t value);
NX_API int NXStringCreateWithInt64(nx_string_t *new_value, int64_t value);
NX_API int NXStringCreateWithBytes(nx_string_t *new_string, const void *data, size_t len, nx_charset_t charset);
NX_API int NXStringCreateEmpty(nx_string_t *new_string);
NX_API int NXStringCreateWithFormatting(nx_string_t *new_string, const char *format, ...);
NX_API size_t NXStringGetLength(nx_string_t string);
/* returns strcmp style return. compare_to is treated as an ASCII string.
if compare_to has non-ASCII characters, results are undetermined */
NX_API int NXStringKeywordCompareWithCString(nx_string_t string, const char *compare_to);
NX_API int NXStringKeywordCompare(nx_string_t string, nx_string_t compare_to);
NX_API int NXStringKeywordCaseCompare(nx_string_t string, nx_string_t compare_to);
/* creates an NXString with the base path from the passed in filename (retains an appended \) */
NX_API int NXStringCreateBasePathFromFilename(nx_string_t filename, nx_string_t *basepath);
NX_API int NXStringGetCString(nx_string_t string, char *user_buffer, size_t user_buffer_length, const char **out_cstring, size_t *out_cstring_length);
NX_API int NXStringGetDoubleValue(nx_string_t string, double *value);
NX_API int NXStringGetIntegerValue(nx_string_t string, int *value);
NX_API int NXStringGetGUIDValue(nx_string_t string, GUID *guid);
static const int nx_string_get_bytes_size_null_terminate = 1; // pass this to null terminate the string
/* returns byte count with enough room to store a converted string
note: if this returns NErr_DirectPointer, you can call NXStringGetBytesDirect to directly retrieve a pointer. */
NX_API int NXStringGetBytesSize(size_t *byte_count, nx_string_t string, nx_charset_t charset, int flags);
/* if possible, retrieves a pointer to bytes.
the length returned depends on whether or not you passed nx_string_get_bytes_size_null_terminate
note: the pointer you get will be invalid after you call NXStringRelease on the string passed in */
NX_API int NXStringGetBytesDirect(const void **bytes, size_t *length, nx_string_t string, nx_charset_t charset, int flags);
NX_API int NXStringGetBytes(size_t *bytes_copied, nx_string_t string, void *bytes, size_t length, nx_charset_t charset, int flags);
NX_API nx_compare_result NXStringCompare(nx_string_t string1, nx_string_t string2, nx_compare_options options);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,25 @@
#include "nxthread.h"
#include "foundation/error.h"
int NXThreadCreate(nx_thread_t *thread, nx_thread_func_t thread_function, nx_thread_parameter_t parameter)
{
*thread = (nx_thread_t)CreateThread(0, 0, thread_function, parameter, 0, 0);
return NErr_Success;
}
int NXThreadJoin(nx_thread_t t, nx_thread_return_t *retval)
{
if (!t)
return NErr_NullPointer;
WaitForSingleObject((HANDLE)t, INFINITE);
if (retval)
GetExitCodeThread((HANDLE)t, retval);
CloseHandle((HANDLE)t);
return NErr_Success;
}
int NXThreadCurrentSetPriority(int priority)
{
SetThreadPriority(GetCurrentThread(), priority);
return NErr_Success;
}

View File

@ -0,0 +1,32 @@
#pragma once
#include "nx/nxapi.h"
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct nx_thread_struct_t { size_t dummy; } *nx_thread_t;
typedef DWORD nx_thread_return_t;
typedef void *nx_thread_parameter_t;
#define NXTHREADCALL CALLBACK
typedef nx_thread_return_t (NXTHREADCALL *nx_thread_func_t)(nx_thread_parameter_t parameter);
// TODO: add parameters for things like stack size
NX_API int NXThreadCreate(nx_thread_t *thread, nx_thread_func_t thread_function, nx_thread_parameter_t parameter);
NX_API int NXThreadJoin(nx_thread_t t, nx_thread_return_t *retval);
enum
{
NX_THREAD_PRIORITY_PLAYBACK=THREAD_PRIORITY_HIGHEST,
};
// sets priority of current thread
NX_API int NXThreadCurrentSetPriority(int priority);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,10 @@
#pragma once
#include "../../foundation/types.h"
#include "../../nx/nxapi.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef uint64_t nx_time_unix_64_t;
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,258 @@
#include "nxuri.h"
#include <stdlib.h>
#include "foundation/atomics.h"
#include "foundation/error.h"
#include "nxstring.h" // for string_heap
#include "foundation/atomics.h"
HANDLE string_heap;
int NXStringFree(HANDLE heap, nx_string_t str);
nx_uri_t NXURIRetain(nx_uri_t string)
{
if (!string)
{
return 0;
}
nx_atomic_inc(&string->ref_count);
return string;
}
void NXURIRelease(nx_uri_t string)
{
if (string)
{
if (nx_atomic_dec(&string->ref_count) == 0)
{
NXStringFree(string_heap, (nx_string_t)string);
}
}
}
// don't include null terminator here
nx_uri_t NXURIMalloc(size_t characters)
{
return (nx_uri_t)NXStringMalloc(characters);
}
int NXURICreateWithNXString(nx_uri_t *uri, nx_string_t string)
{
if (!string)
{
return NErr_Empty;
}
*uri = NXURIRetain((nx_uri_t)string);
return NErr_Success;
}
int NXURICreateFromPath(nx_uri_t *uri, const wchar_t *filename, const nx_uri_t path)
{
size_t filename_length = wcslen(filename);
size_t path_length = path->len;
size_t total_length = filename_length + path_length; /* TODO: check for overflow */
int need_slash = 1;
nx_uri_t output=0;
if (path_length && (path->string[path_length-1] == '/' || path->string[path_length-1] == '\\'))
{
need_slash=0;
}
else
{
total_length++; /* TODO: check for overflow */
}
output = NXURIMalloc(total_length);
if (!output)
{
return NErr_OutOfMemory;
}
wmemcpy(output->string, path->string, path_length);
if (need_slash)
{
output->string[path_length]='\\'; /* TODO: URL detection to know whether to add / or \\ */
wcscpy(&output->string[path_length+1], filename);
}
else
{
wcscpy(&output->string[path_length], filename);
}
*uri = output;
return NErr_Success;
}
int NXURICreateWithPath(nx_uri_t *uri, const nx_uri_t filename, const nx_uri_t path)
{
size_t filename_length = filename->len;
size_t path_length = path->len;
size_t total_length = filename_length + path_length; /* TODO: check for overflow */
int need_slash = 1;
nx_uri_t output=0;
if (path_length && (path->string[path_length-1] == '/' || path->string[path_length-1] == '\\'))
{
need_slash=0;
}
else
{
total_length++; /* TODO: check for overflow */
}
output = NXURIMalloc(total_length);
if (!output)
{
return NErr_OutOfMemory;
}
wmemcpy(output->string, path->string, path_length);
if (need_slash)
{
output->string[path_length]='\\'; /* TODO: URL detection to know whether to add / or \\ */
wcscpy(&output->string[path_length+1], filename->string);
}
else
{
wcscpy(&output->string[path_length], filename->string);
}
*uri = output;
return NErr_Success;
}
int NXURIGetNXString(nx_string_t *string, nx_uri_t uri)
{
*string = (nx_string_t)NXURIRetain(uri);
return NErr_Success;
}
static const wchar_t *FindFilename(nx_uri_t filename)
{
size_t position;
if (!filename || !filename->string || !filename->len)
{
return 0;
}
position=filename->len;
while (position--)
{
wchar_t c = filename->string[position];
if (c == '/' || c == '\\')
{
return &filename->string[position+1];
}
}
return 0;
}
int NXURICreateTempForFilepath(nx_uri_t *out_temp, nx_uri_t filename)
{
nx_uri_t new_uri;
size_t path_length = 0;
wchar_t temp_part[64] = {0};
#if _WIN32_WINNT >= 0x600
int temp_length = wsprintf(temp_part, L".%x-%I64x-%d.tmp", GetCurrentThreadId(), GetTickCount64(), rand());
#else
int temp_length = wsprintf(temp_part, L".%x-%Ix-%d.tmp", GetCurrentThreadId(), GetTickCount(), rand());
#endif
const wchar_t *filepart = FindFilename(filename);
if (filepart)
{
path_length = (filepart - filename->string);
}
else
{
path_length=0;
}
new_uri = NXURIMalloc(path_length+temp_length);
if (!new_uri)
{
return NErr_OutOfMemory;
}
wmemcpy(new_uri->string, filename->string, path_length);
wmemcpy(new_uri->string+path_length, temp_part, temp_length);
new_uri->string[path_length+temp_length]=0;
*out_temp = new_uri;
return NErr_Success;
}
int NXURICreateWithUTF8(nx_uri_t *value, const char *utf8)
{
nx_string_t nx_filename;
nx_uri_t uri_filename;
int ret = NXStringCreateWithUTF8(&nx_filename, utf8);
if (ret != NErr_Success)
{
return ret;
}
ret = NXURICreateWithNXString(&uri_filename, nx_filename);
NXStringRelease(nx_filename);
if (ret != NErr_Success)
return ret;
*value = uri_filename;
return NErr_Success;
}
int NXURICreateRemovingFilename(nx_uri_t *out_uri, nx_uri_t filename)
{
nx_uri_t new_uri;
size_t path_length;
const wchar_t *filepart = FindFilename(filename);
if (filepart)
{
path_length = (filepart - filename->string);
}
else
{
path_length=0;
}
new_uri = NXURIMalloc(path_length);
if (!new_uri)
{
return NErr_OutOfMemory;
}
wmemcpy(new_uri->string, filename->string, path_length);
new_uri->string[path_length]=0;
*out_uri = new_uri;
return NErr_Success;
}
int NXURICreateTemp(nx_uri_t *out_temp)
{
return NXURICreateTempWithExtension(out_temp, "tmp");
}
int NXURICreateTempWithExtension(nx_uri_t *out_temp, const char *extension)
{
nx_uri_t new_uri;
wchar_t temppath[MAX_PATH-14] = {0}; // MAX_PATH-14 'cause MSDN said so
int path_length = GetTempPathW(MAX_PATH-14, temppath);
wchar_t temp_part[64] = {0};
#if _WIN32_WINNT >= 0x600
int temp_length = wsprintf(temp_part, L".%x-%I64x-%d.%S", GetCurrentThreadId(), GetTickCount64(), rand(), extension);
#else
int temp_length = wsprintf(temp_part, L".%x-%Ix-%d.%S", GetCurrentThreadId(), GetTickCount(), rand(), extension);
#endif
new_uri = NXURIMalloc(path_length+temp_length);
if (!new_uri)
{
return NErr_OutOfMemory;
}
wmemcpy(new_uri->string, temppath, path_length);
wmemcpy(new_uri->string+path_length, temp_part, temp_length);
new_uri->string[path_length+temp_length]=0;
*out_temp = new_uri;
return NErr_Success;
}
size_t NXURIGetLength(nx_uri_t string)
{
return (string ? string->len : 0);
}

View File

@ -0,0 +1,44 @@
#pragma once
#include "../../foundation/types.h"
#include "../../nx/nxapi.h"
#include "../../nx/nxstring.h"
#ifdef __cplusplus
extern "C" {
#endif
/* this thing is meant to be identical to nx_string_t, but we want to try to protect it so they don't get easily cast to nx_string_t
since they aren't the same on all platforms */
typedef struct nx_uri_struct_t
{
size_t ref_count;
size_t len;
wchar_t string[1]; // utf-16
} nx_uri_struct_t, *nx_uri_t;
NX_API nx_uri_t NXURIRetain(nx_uri_t string);
NX_API void NXURIRelease(nx_uri_t string);
NX_API nx_uri_t NXURIMalloc(size_t characters);
NX_API int NXURICreateWithNXString(nx_uri_t *uri, nx_string_t string);
NX_API int NXURICreateFromPath(nx_uri_t *uri, const wchar_t *filename, const nx_uri_t path); // windows only
NX_API int NXURICreateWithPath(nx_uri_t *uri, const nx_uri_t filename, const nx_uri_t path);
NX_API int NXURICreateWithNXString(nx_uri_t *new_value, nx_string_t string);
NX_API int NXURICreateTempForFilepath(nx_uri_t *out_temp, nx_uri_t filename);
NX_API int NXURICreateTempWithExtension(nx_uri_t *out_temp, const char *extension);
NX_API int NXURICreateWithUTF8(nx_uri_t *value, const char *utf8);
NX_API int NXURICreateTemp(nx_uri_t *out_temp);
// replaces only the filename portion of the path with the desired filename
// e.g., filepath = /path/to/1.mp3, *out_uri = /path/to/
NX_API int NXURICreateRemovingFilename(nx_uri_t *out_uri, nx_uri_t filename);
NX_API int NXURIGetNXString(nx_string_t *string, nx_uri_t uri);
NX_API size_t NXURIGetLength(nx_uri_t string);
#ifdef __cplusplus
}
#endif