diff options
-rw-r--r-- | chrome/browser/sync/engine/syncapi.cc | 1 | ||||
-rw-r--r-- | chrome/browser/sync/syncable/syncable.cc | 26 | ||||
-rw-r--r-- | chrome/browser/sync/syncable/syncable.h | 20 | ||||
-rw-r--r-- | chrome/browser/sync/syncable/syncable_unittest.cc | 398 |
4 files changed, 234 insertions, 211 deletions
diff --git a/chrome/browser/sync/engine/syncapi.cc b/chrome/browser/sync/engine/syncapi.cc index 969b4fb..397e552 100644 --- a/chrome/browser/sync/engine/syncapi.cc +++ b/chrome/browser/sync/engine/syncapi.cc @@ -41,6 +41,7 @@ #include "chrome/browser/sync/util/crypto_helpers.h" #include "chrome/browser/sync/util/event_sys.h" #include "chrome/browser/sync/util/path_helpers.h" +#include "chrome/browser/sync/util/pthread_helpers.h" #include "chrome/browser/sync/util/user_settings.h" #include "googleurl/src/gurl.h" diff --git a/chrome/browser/sync/syncable/syncable.cc b/chrome/browser/sync/syncable/syncable.cc index aa4832e..4d7b1c0 100644 --- a/chrome/browser/sync/syncable/syncable.cc +++ b/chrome/browser/sync/syncable/syncable.cc @@ -202,7 +202,6 @@ Directory::Kernel::Kernel(const PathString& db_path, next_metahandle(info.max_metahandle + 1), next_id(info.kernel_info.next_id) { info_status_ = Directory::KERNEL_SHARE_INFO_VALID; - CHECK(0 == pthread_mutex_init(&mutex, NULL)); } inline void DeleteEntry(EntryKernel* kernel) { @@ -222,7 +221,6 @@ Directory::Kernel::~Kernel() { CHECK(0 == refcount); delete channel; delete changes_channel; - CHECK(0 == pthread_mutex_destroy(&mutex)); delete unsynced_metahandles; delete unapplied_update_metahandles; delete extended_attributes; @@ -764,7 +762,9 @@ void Directory::TakeSnapshotForSaveChanges(SaveChangesSnapshot* snapshot) { bool Directory::SaveChanges() { bool success = false; DCHECK(store_); - PThreadScopedLock<PThreadMutex> lock(&kernel_->save_changes_mutex); + + AutoLock scoped_lock(kernel_->save_changes_mutex); + // Snapshot and save. SaveChangesSnapshot snapshot; TakeSnapshotForSaveChanges(&snapshot); @@ -1104,22 +1104,7 @@ void Directory::CheckTreeInvariants(syncable::BaseTransaction* trans, // ScopedKernelLocks ScopedKernelLock::ScopedKernelLock(const Directory* dir) - : dir_(const_cast<Directory*>(dir)) { - // Swap out the dbhandle to enforce the "No IO while holding kernel - // lock" rule. - // HA!! Yeah right. What about your pre-cached queries :P - pthread_mutex_lock(&dir->kernel_->mutex); -} -ScopedKernelLock::~ScopedKernelLock() { - pthread_mutex_unlock(&dir_->kernel_->mutex); -} - -ScopedKernelUnlock::ScopedKernelUnlock(ScopedKernelLock* lock) - : lock_(lock) { - pthread_mutex_unlock(&lock->dir_->kernel_->mutex); -} -ScopedKernelUnlock::~ScopedKernelUnlock() { - pthread_mutex_lock(&lock_->dir_->kernel_->mutex); + : scoped_lock_(dir->kernel_->mutex), dir_(const_cast<Directory*>(dir)) { } /////////////////////////////////////////////////////////////////////////// @@ -1167,7 +1152,7 @@ void BaseTransaction::UnlockAndLog(OriginalEntries* originals_arg) { return; } - dirkernel_->changes_channel_mutex.Lock(); + AutoLock scoped_lock(dirkernel_->changes_channel_mutex); // Tell listeners to calculate changes while we still have the mutex. DirectoryChangeEvent event = { DirectoryChangeEvent::CALCULATE_CHANGES, originals.get(), this, writer_ }; @@ -1179,7 +1164,6 @@ void BaseTransaction::UnlockAndLog(OriginalEntries* originals_arg) { { DirectoryChangeEvent::TRANSACTION_COMPLETE, NULL, NULL, INVALID }; dirkernel_->changes_channel->NotifyListeners(complete_event); - dirkernel_->changes_channel_mutex.Unlock(); } ReadTransaction::ReadTransaction(Directory* directory, const char* file, diff --git a/chrome/browser/sync/syncable/syncable.h b/chrome/browser/sync/syncable/syncable.h index 086d169..9e55348 100644 --- a/chrome/browser/sync/syncable/syncable.h +++ b/chrome/browser/sync/syncable/syncable.h @@ -23,11 +23,9 @@ #include "chrome/browser/sync/syncable/path_name_cmp.h" #include "chrome/browser/sync/syncable/syncable_id.h" #include "chrome/browser/sync/util/compat_file.h" -#include "chrome/browser/sync/util/compat_pthread.h" #include "chrome/browser/sync/util/dbgq.h" #include "chrome/browser/sync/util/event_sys.h" #include "chrome/browser/sync/util/path_helpers.h" -#include "chrome/browser/sync/util/pthread_helpers.h" #include "chrome/browser/sync/util/row_iterator.h" #include "chrome/browser/sync/util/sync_types.h" @@ -797,7 +795,6 @@ struct ExtendedAttributeValue { typedef std::map<ExtendedAttributeKey, ExtendedAttributeValue> ExtendedAttributes; -typedef PThreadScopedLock<PThreadMutex> ScopedTransactionLock; typedef std::set<int64> MetahandleSet; // A list of metahandles whose metadata should not be purged. @@ -1114,7 +1111,7 @@ class Directory { // // Never hold the mutex and do anything with the database or any // other buffered IO. Violating this rule will result in deadlock. - pthread_mutex_t mutex; // TODO(chron): Swap this out for Chrome Lock + Lock mutex; MetahandlesIndex* metahandles_index; // Entries indexed by metahandle IdsIndex* ids_index; // Entries indexed by id ParentIdAndNamesIndex* parent_id_and_names_index; @@ -1137,7 +1134,7 @@ class Directory { // while holding the transaction mutex and released after // releasing the transaction mutex. ChangesChannel* const changes_channel; - PThreadMutex changes_channel_mutex; + Lock changes_channel_mutex; KernelShareInfoStatus info_status_; // These 5 members are backed in the share_info table, and // their state is marked by the flag above. @@ -1154,7 +1151,7 @@ class Directory { // It doesn't make sense for two threads to run SaveChanges at the same // time; this mutex protects that activity. - PThreadMutex save_changes_mutex; + Lock save_changes_mutex; // The next metahandle and id are protected by kernel mutex. int64 next_metahandle; @@ -1173,20 +1170,13 @@ class Directory { class ScopedKernelLock { public: explicit ScopedKernelLock(const Directory*); - ~ScopedKernelLock(); + ~ScopedKernelLock() {} + AutoLock scoped_lock_; Directory* const dir_; DISALLOW_COPY_AND_ASSIGN(ScopedKernelLock); }; -class ScopedKernelUnlock { - public: - explicit ScopedKernelUnlock(ScopedKernelLock* lock); - ~ScopedKernelUnlock(); - ScopedKernelLock* const lock_; - DISALLOW_COPY_AND_ASSIGN(ScopedKernelUnlock); -}; - // Transactions are now processed FIFO (+overlapping reads). class BaseTransaction { friend class Entry; diff --git a/chrome/browser/sync/syncable/syncable_unittest.cc b/chrome/browser/sync/syncable/syncable_unittest.cc index b1e1b10..cbfcfbe 100644 --- a/chrome/browser/sync/syncable/syncable_unittest.cc +++ b/chrome/browser/sync/syncable/syncable_unittest.cc @@ -31,6 +31,7 @@ #include "base/at_exit.h" #include "base/logging.h" +#include "base/platform_thread.h" #include "base/scoped_ptr.h" #include "chrome/browser/sync/syncable/directory_backing_store.h" #include "chrome/browser/sync/syncable/directory_manager.h" @@ -784,18 +785,27 @@ TEST(SyncableDirectoryManager, TestFileRelease) { ASSERT_TRUE(0 == PathRemove(dm.GetSyncDataDatabasePath())); } -static void* OpenTestThreadMain(void* arg) { - DirectoryManager* const dm = reinterpret_cast<DirectoryManager*>(arg); - CHECK(dm->Open(PSTR("Open"))); - return 0; -} +class ThreadOpenTestDelegate : public PlatformThread::Delegate { + public: + explicit ThreadOpenTestDelegate(DirectoryManager* dm) + : directory_manager_(dm) {} + DirectoryManager* const directory_manager_; + + private: + // PlatformThread::Delegate methods: + virtual void ThreadMain() { + CHECK(directory_manager_->Open(PSTR("Open"))); + } + + DISALLOW_COPY_AND_ASSIGN(ThreadOpenTestDelegate); +}; TEST(SyncableDirectoryManager, ThreadOpenTest) { DirectoryManager dm(PSTR(".")); - pthread_t thread; - ASSERT_TRUE(0 == pthread_create(&thread, 0, OpenTestThreadMain, &dm)); - void* result; - ASSERT_TRUE(0 == pthread_join(thread, &result)); + PlatformThreadHandle thread_handle; + ThreadOpenTestDelegate test_delegate(&dm); + ASSERT_TRUE(PlatformThread::Create(0, &test_delegate, &thread_handle)); + PlatformThread::Join(thread_handle); { ScopedDirLookup dir(&dm, PSTR("Open")); ASSERT_TRUE(dir.good()); @@ -805,107 +815,114 @@ TEST(SyncableDirectoryManager, ThreadOpenTest) { ASSERT_FALSE(dir.good()); } -namespace ThreadBug1 { - struct Step { - PThreadMutex mutex; - PThreadCondVar condvar; - int number; - int64 metahandle; - }; - struct ThreadArg { - int role; // 0 or 1, meaning this thread does the odd or event steps. - Step* step; - DirectoryManager* dirman; - }; +struct Step { + Step() : condvar(&mutex), number(0) {} - void* ThreadMain(void* arg) { - ThreadArg* const args = reinterpret_cast<ThreadArg*>(arg); - const int role = args->role; - Step* const step = args->step; - DirectoryManager* const dirman = args->dirman; - const PathString dirname = PSTR("ThreadBug1"); - PThreadScopedLock<PThreadMutex> lock(&step->mutex); - while (step->number < 3) { - while (step->number % 2 != role) - pthread_cond_wait(&step->condvar.condvar_, &step->mutex.mutex_); - switch (step->number) { + Lock mutex; + ConditionVariable condvar; + int number; + int64 metahandle; +}; + +class ThreadBugDelegate : public PlatformThread::Delegate { + public: + // a role is 0 or 1, meaning this thread does the odd or event steps. + ThreadBugDelegate(int role, Step* step, DirectoryManager* dirman) + : role_(role), step_(step), directory_manager_(dirman) {} + + protected: + const int role_; + Step* const step_; + DirectoryManager* const directory_manager_; + + // PlatformThread::Delegate methods: + virtual void ThreadMain() { + const PathString dirname = PSTR("ThreadBug1"); + AutoLock scoped_lock(step_->mutex); + + while (step_->number < 3) { + while (step_->number % 2 != role_) { + step_->condvar.Wait(); + } + switch (step_->number) { case 0: - dirman->Open(dirname); + directory_manager_->Open(dirname); break; case 1: { - dirman->Close(dirname); - dirman->Open(dirname); - ScopedDirLookup dir(dirman, dirname); + directory_manager_->Close(dirname); + directory_manager_->Open(dirname); + ScopedDirLookup dir(directory_manager_, dirname); CHECK(dir.good()); WriteTransaction trans(dir, UNITTEST, __FILE__, __LINE__); MutableEntry me(&trans, CREATE, trans.root_id(), PSTR("Jeff")); - step->metahandle = me.Get(META_HANDLE); + step_->metahandle = me.Get(META_HANDLE); me.Put(IS_UNSYNCED, true); } break; case 2: { - ScopedDirLookup dir(dirman, dirname); + ScopedDirLookup dir(directory_manager_, dirname); CHECK(dir.good()); ReadTransaction trans(dir, __FILE__, __LINE__); - Entry e(&trans, GET_BY_HANDLE, step->metahandle); + Entry e(&trans, GET_BY_HANDLE, step_->metahandle); CHECK(e.good()); // Failed due to ThreadBug1 } - dirman->Close(dirname); + directory_manager_->Close(dirname); break; - } - step->number += 1; - pthread_cond_signal(&step->condvar.condvar_); - } - return 0; - } -} + } + step_->number += 1; + step_->condvar.Signal(); + } + } -TEST(SyncableDirectoryManager, ThreadBug1) { - using ThreadBug1::Step; - using ThreadBug1::ThreadArg; - using ThreadBug1::ThreadMain; + DISALLOW_COPY_AND_ASSIGN(ThreadBugDelegate); +}; +TEST(SyncableDirectoryManager, ThreadBug1) { Step step; step.number = 0; DirectoryManager dirman(PSTR(".")); - ThreadArg arg1 = { 0, &step, &dirman }; - ThreadArg arg2 = { 1, &step, &dirman }; - pthread_t thread1, thread2; - ASSERT_TRUE(0 == pthread_create(&thread1, NULL, &ThreadMain, &arg1)); - ASSERT_TRUE(0 == pthread_create(&thread2, NULL, &ThreadMain, &arg2)); - void* retval; - ASSERT_TRUE(0 == pthread_join(thread1, &retval)); - ASSERT_TRUE(0 == pthread_join(thread2, &retval)); + ThreadBugDelegate thread_delegate_1(0, &step, &dirman); + ThreadBugDelegate thread_delegate_2(1, &step, &dirman); + + PlatformThreadHandle thread_handle_1; + PlatformThreadHandle thread_handle_2; + + ASSERT_TRUE(PlatformThread::Create(0, &thread_delegate_1, &thread_handle_1)); + ASSERT_TRUE(PlatformThread::Create(0, &thread_delegate_2, &thread_handle_2)); + + PlatformThread::Join(thread_handle_1); + PlatformThread::Join(thread_handle_2); } -namespace DirectoryKernelStalenessBug { - // The in-memory information would get out of sync because a - // directory would be closed and re-opened, and then an old - // Directory::Kernel with stale information would get saved to the db. - typedef ThreadBug1::Step Step; - typedef ThreadBug1::ThreadArg ThreadArg; - void* ThreadMain(void* arg) { +// The in-memory information would get out of sync because a +// directory would be closed and re-opened, and then an old +// Directory::Kernel with stale information would get saved to the db. +class DirectoryKernelStalenessBugDelegate : public ThreadBugDelegate { + public: + DirectoryKernelStalenessBugDelegate(int role, Step* step, + DirectoryManager* dirman) + : ThreadBugDelegate(role, step, dirman) {} + + virtual void ThreadMain() { const char test_bytes[] = "test data"; - ThreadArg* const args = reinterpret_cast<ThreadArg*>(arg); - const int role = args->role; - Step* const step = args->step; - DirectoryManager* const dirman = args->dirman; const PathString dirname = PSTR("DirectoryKernelStalenessBug"); - PThreadScopedLock<PThreadMutex> lock(&step->mutex); - while (step->number < 4) { - while (step->number % 2 != role) - pthread_cond_wait(&step->condvar.condvar_, &step->mutex.mutex_); - switch (step->number) { + AutoLock scoped_lock(step_->mutex); + + while (step_->number < 4) { + while (step_->number % 2 != role_) { + step_->condvar.Wait(); + } + switch (step_->number) { case 0: { // Clean up remnants of earlier test runs. - PathRemove(dirman->GetSyncDataDatabasePath()); + PathRemove(directory_manager_->GetSyncDataDatabasePath()); // Test. - dirman->Open(dirname); - ScopedDirLookup dir(dirman, dirname); + directory_manager_->Open(dirname); + ScopedDirLookup dir(directory_manager_, dirname); CHECK(dir.good()); WriteTransaction trans(dir, UNITTEST, __FILE__, __LINE__); MutableEntry me(&trans, CREATE, trans.root_id(), PSTR("Jeff")); @@ -915,28 +932,28 @@ namespace DirectoryKernelStalenessBug { sizeof(test_bytes)); } { - ScopedDirLookup dir(dirman, dirname); + ScopedDirLookup dir(directory_manager_, dirname); CHECK(dir.good()); dir->SaveChanges(); } - dirman->Close(dirname); + directory_manager_->Close(dirname); break; case 1: { - dirman->Open(dirname); - ScopedDirLookup dir(dirman, dirname); + directory_manager_->Open(dirname); + ScopedDirLookup dir(directory_manager_, dirname); CHECK(dir.good()); } break; case 2: { - ScopedDirLookup dir(dirman, dirname); + ScopedDirLookup dir(directory_manager_, dirname); CHECK(dir.good()); } break; case 3: { - ScopedDirLookup dir(dirman, dirname); + ScopedDirLookup dir(directory_manager_, dirname); CHECK(dir.good()); ReadTransaction trans(dir, __FILE__, __LINE__); Entry e(&trans, GET_BY_PATH, PSTR("Jeff")); @@ -944,120 +961,151 @@ namespace DirectoryKernelStalenessBug { sizeof(test_bytes)); } // Same result as CloseAllDirectories, but more code coverage. - dirman->Close(dirname); + directory_manager_->Close(dirname); break; } - step->number += 1; - pthread_cond_signal(&step->condvar.condvar_); + step_->number += 1; + step_->condvar.Signal(); } - return 0; } -} -TEST(SyncableDirectoryManager, DirectoryKernelStalenessBug) { - using DirectoryKernelStalenessBug::Step; - using DirectoryKernelStalenessBug::ThreadArg; - using DirectoryKernelStalenessBug::ThreadMain; + DISALLOW_COPY_AND_ASSIGN(DirectoryKernelStalenessBugDelegate); +}; +TEST(SyncableDirectoryManager, DirectoryKernelStalenessBug) { Step step; - step.number = 0; - DirectoryManager dirman(PSTR(".")); - ThreadArg arg1 = { 0, &step, &dirman }; - ThreadArg arg2 = { 1, &step, &dirman }; - pthread_t thread1, thread2; - ASSERT_TRUE(0 == pthread_create(&thread1, NULL, &ThreadMain, &arg1)); - ASSERT_TRUE(0 == pthread_create(&thread2, NULL, &ThreadMain, &arg2)); - void* retval; - ASSERT_TRUE(0 == pthread_join(thread1, &retval)); - ASSERT_TRUE(0 == pthread_join(thread2, &retval)); -} -timespec operator + (const timespec& a, const timespec& b) { - const long nanos = a.tv_nsec + b.tv_nsec; - static const long nanos_per_second = 1000000000; - timespec r = { a.tv_sec + b.tv_sec + (nanos / nanos_per_second), - nanos % nanos_per_second }; - return r; -} + DirectoryManager dirman(PSTR(".")); + DirectoryKernelStalenessBugDelegate thread_delegate_1(0, &step, &dirman); + DirectoryKernelStalenessBugDelegate thread_delegate_2(1, &step, &dirman); -void SleepMs(int milliseconds) { -#ifdef OS_WIN - Sleep(milliseconds); -#else - usleep(milliseconds * 1000); -#endif -} + PlatformThreadHandle thread_handle_1; + PlatformThreadHandle thread_handle_2; -namespace StressTransaction { - struct Globals { - DirectoryManager* dirman; - PathString dirname; - }; + ASSERT_TRUE(PlatformThread::Create(0, &thread_delegate_1, &thread_handle_1)); + ASSERT_TRUE(PlatformThread::Create(0, &thread_delegate_2, &thread_handle_2)); - struct ThreadArg { - Globals* globals; - int thread_number; - }; + PlatformThread::Join(thread_handle_1); + PlatformThread::Join(thread_handle_2); +} - void* ThreadMain(void* arg) { - ThreadArg* const args = reinterpret_cast<ThreadArg*>(arg); - Globals* const globals = args->globals; - ScopedDirLookup dir(globals->dirman, globals->dirname); - CHECK(dir.good()); - int entry_count = 0; - PathString path_name; - for (int i = 0; i < 20; ++i) { - const int rand_action = rand() % 10; - if (rand_action < 4 && !path_name.empty()) { - ReadTransaction trans(dir, __FILE__, __LINE__); - Entry e(&trans, GET_BY_PARENTID_AND_NAME, trans.root_id(), path_name); - SleepMs(rand() % 10); - CHECK(e.good()); - } else { - string unique_name = StringPrintf("%d.%d", args->thread_number, - entry_count++); - path_name.assign(unique_name.begin(), unique_name.end()); - WriteTransaction trans(dir, UNITTEST, __FILE__, __LINE__); - MutableEntry e(&trans, CREATE, trans.root_id(), path_name); - CHECK(e.good()); - SleepMs(rand() % 20); - e.Put(IS_UNSYNCED, true); - if (e.Put(ID, TestIdFactory::FromNumber(rand())) && - e.Get(ID).ServerKnows() && !e.Get(ID).IsRoot()) - e.Put(BASE_VERSION, 1); - } +class StressTransactionsDelegate : public PlatformThread::Delegate { + public: + StressTransactionsDelegate(DirectoryManager* dm, PathString dirname, + int thread_number) + : directory_manager_(dm), dirname_(dirname), + thread_number_(thread_number) {} + + private: + DirectoryManager* const directory_manager_; + PathString dirname_; + const int thread_number_; + + // PlatformThread::Delegate methods: + virtual void ThreadMain() { + ScopedDirLookup dir(directory_manager_, dirname_); + CHECK(dir.good()); + int entry_count = 0; + PathString path_name; + + for (int i = 0; i < 20; ++i) { + const int rand_action = rand() % 10; + if (rand_action < 4 && !path_name.empty()) { + ReadTransaction trans(dir, __FILE__, __LINE__); + Entry e(&trans, GET_BY_PARENTID_AND_NAME, trans.root_id(), path_name); + PlatformThread::Sleep(rand() % 10); + CHECK(e.good()); + } else { + string unique_name = StringPrintf("%d.%d", thread_number_, + entry_count++); + path_name.assign(unique_name.begin(), unique_name.end()); + WriteTransaction trans(dir, UNITTEST, __FILE__, __LINE__); + MutableEntry e(&trans, CREATE, trans.root_id(), path_name); + CHECK(e.good()); + PlatformThread::Sleep(rand() % 20); + e.Put(IS_UNSYNCED, true); + if (e.Put(ID, TestIdFactory::FromNumber(rand())) && + e.Get(ID).ServerKnows() && !e.Get(ID).IsRoot()) + e.Put(BASE_VERSION, 1); + } } - return 0; - } -} + } -TEST(SyncableDirectory, StressTransactions) { - using StressTransaction::Globals; - using StressTransaction::ThreadArg; - using StressTransaction::ThreadMain; + DISALLOW_COPY_AND_ASSIGN(StressTransactionsDelegate); +}; +TEST(SyncableDirectory, StressTransactions) { DirectoryManager dirman(PSTR(".")); - Globals globals; - globals.dirname = PSTR("stress"); - globals.dirman = &dirman; + PathString dirname = PSTR("stress"); PathRemove(dirman.GetSyncDataDatabasePath()); - dirman.Open(globals.dirname); + dirman.Open(dirname); + const int kThreadCount = 7; - pthread_t threads[kThreadCount]; - ThreadArg thread_args[kThreadCount]; + PlatformThreadHandle threads[kThreadCount]; + scoped_ptr<StressTransactionsDelegate> thread_delegates[kThreadCount]; + + for (int i = 0; i < kThreadCount; ++i) { + thread_delegates[i].reset( + new StressTransactionsDelegate(&dirman, dirname, i)); + ASSERT_TRUE( + PlatformThread::Create(0, thread_delegates[i].get(), &threads[i])); + } + for (int i = 0; i < kThreadCount; ++i) { - thread_args[i].thread_number = i; - thread_args[i].globals = &globals; - ASSERT_TRUE(0 == pthread_create(threads + i, NULL, &ThreadMain, - thread_args + i)); + PlatformThread::Join(threads[i]); } - void* retval; - for (pthread_t* i = threads; i < threads + kThreadCount; ++i) - ASSERT_TRUE(0 == pthread_join(*i, &retval)); - dirman.Close(globals.dirname); + + dirman.Close(dirname); PathRemove(dirman.GetSyncDataDatabasePath()); } +static PathString UTF8ToPathStringQuick(const char* str) { + PathString ret; + CHECK(browser_sync::UTF8ToPathString(str, strlen(str), &ret)); + return ret; +} + +// Returns number of chars used. Max possible is 4. +// This algorithm was coded from the table at +// http://en.wikipedia.org/w/index.php?title=UTF-8&oldid=153391259 +// There are no endian issues. +static int UTF32ToUTF8(uint32 incode, unsigned char* out) { + if (incode <= 0x7f) { + out[0] = incode; + return 1; + } + if (incode <= 0x7ff) { + out[0] = 0xC0; + out[0] |= (incode >> 6); + out[1] = 0x80; + out[1] |= (incode & 0x3F); + return 2; + } + if (incode <= 0xFFFF) { + if ((incode > 0xD7FF) && (incode < 0xE000)) + return 0; + out[0] = 0xE0; + out[0] |= (incode >> 12); + out[1] = 0x80; + out[1] |= (incode >> 6) & 0x3F; + out[2] = 0x80; + out[2] |= incode & 0x3F; + return 3; + } + if (incode <= 0x10FFFF) { + out[0] = 0xF0; + out[0] |= incode >> 18; + out[1] = 0x80; + out[1] |= (incode >> 12) & 0x3F; + out[2] = 0x80; + out[2] |= (incode >> 6) & 0x3F; + out[3] = 0x80; + out[3] |= incode & 0x3F; + return 4; + } + return 0; +} + TEST(Syncable, ComparePathNames) { struct { char a; |