diff options
Diffstat (limited to 'chrome/browser/sync/notifier/base')
28 files changed, 3038 insertions, 0 deletions
diff --git a/chrome/browser/sync/notifier/base/async_dns_lookup.cc b/chrome/browser/sync/notifier/base/async_dns_lookup.cc new file mode 100644 index 0000000..0d3ce87 --- /dev/null +++ b/chrome/browser/sync/notifier/base/async_dns_lookup.cc @@ -0,0 +1,133 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/sync/notifier/base/async_dns_lookup.h" + +#ifdef POSIX +#include <arpa/inet.h> +#include <netinet/in.h> +#include <netinet/ip.h> +#include <netdb.h> +#include <sys/socket.h> +#include <sys/types.h> +#endif // POSIX + +#include <vector> + +#include "chrome/browser/sync/notifier/base/nethelpers.h" +#include "chrome/browser/sync/notifier/gaia_auth/inet_aton.h" +#include "talk/base/byteorder.h" +#include "talk/base/common.h" +#include "talk/base/logging.h" +#include "talk/base/socketaddress.h" +#include "talk/base/thread.h" + +enum { MSG_TIMEOUT = talk_base::SignalThread::ST_MSG_FIRST_AVAILABLE }; + +#ifndef WIN32 +const int WSAHOST_NOT_FOUND = 11001; // follows the format in winsock2.h +#endif // WIN32 + +namespace notifier { + +AsyncDNSLookup::AsyncDNSLookup(const talk_base::SocketAddress& server) + : server_(new talk_base::SocketAddress(server)), + error_(0) { + // Timeout after 5 seconds. + talk_base::Thread::Current()->PostDelayed(5000, this, MSG_TIMEOUT); +} + +AsyncDNSLookup::~AsyncDNSLookup() { +} + +void AsyncDNSLookup::DoWork() { + std::string hostname(server_->IPAsString()); + + in_addr addr; + if (inet_aton(hostname.c_str(), &addr)) { + talk_base::CritScope scope(&cs_); + ip_list_.push_back(talk_base::NetworkToHost32(addr.s_addr)); + } else { + LOG_F(LS_VERBOSE) << "(" << hostname << ")"; + hostent ent; + char buffer[8192]; + int errcode = 0; + hostent* host = SafeGetHostByName(hostname.c_str(), &ent, + buffer, sizeof(buffer), + &errcode); + talk_base::Thread::Current()->Clear(this, MSG_TIMEOUT); + if (host) { + talk_base::CritScope scope(&cs_); + + // Check to see if this already timed out. + if (error_ == 0) { + for (int index = 0; true; ++index) { + uint32* addr = reinterpret_cast<uint32*>(host->h_addr_list[index]); + if (addr == 0) { // 0 = end of list + break; + } + uint32 ip = talk_base::NetworkToHost32(*addr); + LOG_F(LS_VERBOSE) << "(" << hostname << ") resolved to: " + << talk_base::SocketAddress::IPToString(ip); + ip_list_.push_back(ip); + } + // Maintain the invariant that either the list is not empty + // or the error is non zero when we are done with processing + // the dnslookup. + if (ip_list_.empty() && error_ == 0) { + error_ = WSAHOST_NOT_FOUND; + } + } + FreeHostEnt(host); + } else { + { // Scoping for the critical section. + talk_base::CritScope scope(&cs_); + + // Check to see if this already timed out. + if (error_ == 0) { + error_ = errcode; + } + } + LOG_F(LS_ERROR) << "(" << hostname << ") error: " << error_; + } + } +} + +void AsyncDNSLookup::OnMessage(talk_base::Message* message) { + ASSERT(message); + if (message->message_id == MSG_TIMEOUT) { + OnTimeout(); + } else { + talk_base::SignalThread::OnMessage(message); + } +} + +void AsyncDNSLookup::OnTimeout() { + // Allow the scope for the critical section to be the whole + // method, just to be sure that the worker thread can't exit + // while we are doing SignalWorkDone (because that could possibly + // cause the class to be deleted). + talk_base::CritScope scope(&cs_); + + // Check to see if the ip list was already filled (or errored out). + if (!ip_list_.empty() || error_ != 0) { + return; + } + + // Worker thread is taking too long so timeout. + error_ = WSAHOST_NOT_FOUND; + + // Rely on the caller to do the Release/Destroy. + // + // Doing this signal while holding cs_ won't cause a deadlock because + // the AsyncDNSLookup::DoWork thread doesn't have any locks at this point, + // and it is the only thread being held up by this. + SignalWorkDone(this); + + // Ensure that no more "WorkDone" signaling is done. + // Don't call Release or Destroy since that was already done + // by the callback. + SignalWorkDone.disconnect_all(); +} +} // namespace notifier diff --git a/chrome/browser/sync/notifier/base/async_dns_lookup.h b/chrome/browser/sync/notifier/base/async_dns_lookup.h new file mode 100644 index 0000000..123d311 --- /dev/null +++ b/chrome/browser/sync/notifier/base/async_dns_lookup.h @@ -0,0 +1,49 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_SYNC_NOTIFIER_BASE_ASYNC_DNS_LOOKUP_H_ +#define CHROME_BROWSER_SYNC_NOTIFIER_BASE_ASYNC_DNS_LOOKUP_H_ + +#include <vector> + +#include "base/scoped_ptr.h" +#include "talk/base/signalthread.h" + +namespace talk_base { +class SocketAddress; +class Task; +} + +namespace notifier { + +class AsyncDNSLookup : public talk_base::SignalThread { + public: + explicit AsyncDNSLookup(const talk_base::SocketAddress& server); + virtual ~AsyncDNSLookup(); + + const int error() const { + return error_; + } + + const std::vector<uint32>& ip_list() const { + return ip_list_; + } + + protected: + // SignalThread Interface + virtual void DoWork(); + virtual void OnMessage(talk_base::Message* message); + + private: + void OnTimeout(); + + scoped_ptr<talk_base::SocketAddress> server_; + talk_base::CriticalSection cs_; + int error_; + std::vector<uint32> ip_list_; + + DISALLOW_COPY_AND_ASSIGN(AsyncDNSLookup); +}; +} // namespace notifier +#endif // CHROME_BROWSER_SYNC_NOTIFIER_BASE_ASYNC_DNS_LOOKUP_H_ diff --git a/chrome/browser/sync/notifier/base/async_network_alive.h b/chrome/browser/sync/notifier/base/async_network_alive.h new file mode 100644 index 0000000..330348d --- /dev/null +++ b/chrome/browser/sync/notifier/base/async_network_alive.h @@ -0,0 +1,52 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_SYNC_NOTIFIER_BASE_ASYNC_NETWORK_ALIVE_H_ +#define CHROME_BROWSER_SYNC_NOTIFIER_BASE_ASYNC_NETWORK_ALIVE_H_ + +#include "talk/base/signalthread.h" + +namespace notifier { + +// System specific info needed for changes +class PlatformNetworkInfo; + +class AsyncNetworkAlive : public talk_base::SignalThread { + public: + static AsyncNetworkAlive* Create(); + + virtual ~AsyncNetworkAlive() {} + + bool alive() const { + return alive_; + } + + bool error() const { + return error_; + } + + void SetWaitForNetworkChange(PlatformNetworkInfo* previous_info) { + network_info_ = previous_info; + } + + PlatformNetworkInfo* ReleaseInfo() { + PlatformNetworkInfo* info = network_info_; + network_info_ = NULL; + return info; + } + + protected: + AsyncNetworkAlive() : network_info_(NULL), alive_(false), error_(false) { + } + + protected: + PlatformNetworkInfo* network_info_; + bool alive_; + bool error_; + + private: + DISALLOW_COPY_AND_ASSIGN(AsyncNetworkAlive); +}; +} // namespace notifier +#endif // CHROME_BROWSER_SYNC_NOTIFIER_BASE_ASYNC_NETWORK_ALIVE_H_ diff --git a/chrome/browser/sync/notifier/base/fastalloc.h b/chrome/browser/sync/notifier/base/fastalloc.h new file mode 100644 index 0000000..ed19a53 --- /dev/null +++ b/chrome/browser/sync/notifier/base/fastalloc.h @@ -0,0 +1,59 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_SYNC_NOTIFIER_BASE_FASTALLOC_H_ +#define CHROME_BROWSER_SYNC_NOTIFIER_BASE_FASTALLOC_H_ + +#include <assert.h> + +namespace notifier { + +template<class T, size_t Size> +class FastAlloc { + public: + FastAlloc() : buffer_(NULL), size_(0) { }; + ~FastAlloc() { freeBuffer(); } + T* get_buffer(size_t size) { + if (size_ != 0) { + // We only allow one call to get_buffer. This makes the logic here + // simpler, and the user has to worry less about someone else calling + // get_buffer again on the same FastAlloc object and invalidating the + // memory they were using. + assert(false && "get_buffer may one be called once"); + return NULL; + } + + if (size <= Size) { + buffer_ = internal_buffer_; + } else { + buffer_ = new T[size]; + } + + if (buffer_ != NULL) { + size_ = size; + } + return buffer_; + } + + private: + void freeBuffer() { +#ifdef DEBUG + memset(buffer_, 0xCC, size_ * sizeof(T)); +#endif + + if (buffer_ != NULL && buffer_ != internal_buffer_) { + delete[] buffer_; + } + buffer_ = NULL; + size_ = 0; + } + + T* buffer_; + T internal_buffer_[Size]; + size_t size_; +}; + +} + +#endif // CHROME_BROWSER_SYNC_NOTIFIER_BASE_FASTALLOC_H_ diff --git a/chrome/browser/sync/notifier/base/linux/network_status_detector_task_linux.cc b/chrome/browser/sync/notifier/base/linux/network_status_detector_task_linux.cc new file mode 100644 index 0000000..e232bcb --- /dev/null +++ b/chrome/browser/sync/notifier/base/linux/network_status_detector_task_linux.cc @@ -0,0 +1,15 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/sync/notifier/base/network_status_detector_task.h" + +namespace notifier { + +NetworkStatusDetectorTask* NetworkStatusDetectorTask::Create( + talk_base::Task* parent) { + // TODO(sync): No implementation for linux + return NULL; +} + +} // namespace notifier diff --git a/chrome/browser/sync/notifier/base/linux/time_linux.cc b/chrome/browser/sync/notifier/base/linux/time_linux.cc new file mode 100644 index 0000000..ea2acf5 --- /dev/null +++ b/chrome/browser/sync/notifier/base/linux/time_linux.cc @@ -0,0 +1,7 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Time functions + +#include "chrome/browser/sync/notifier/base/posix/time_posix.cc" diff --git a/chrome/browser/sync/notifier/base/nethelpers.cc b/chrome/browser/sync/notifier/base/nethelpers.cc new file mode 100644 index 0000000..23fc8d2 --- /dev/null +++ b/chrome/browser/sync/notifier/base/nethelpers.cc @@ -0,0 +1,42 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/sync/notifier/base/nethelpers.h" + +namespace notifier { + +hostent* SafeGetHostByName(const char* hostname, hostent* host, + char* buffer, size_t buffer_len, + int* herrno) { + hostent* result = NULL; +#if WIN32 + result = gethostbyname(hostname); + if (!result) { + *herrno = WSAGetLastError(); + } +#elif OS_LINUX + gethostbyname_r(hostname, host, buffer, buffer_len, &result, herrno); +#elif OSX + result = getipnodebyname(hostname, AF_INET, AI_DEFAULT, herrno); +#else +#error "I don't know how to do gethostbyname safely on your system." +#endif + return result; +} + +// This function should mirror the above function, and free any resources +// allocated by the above. +void FreeHostEnt(hostent* host) { +#if WIN32 + // No need to free anything, struct returned is static memory. +#elif OS_LINUX + // No need to free anything, we pass in a pointer to a struct. +#elif OSX + freehostent(host); +#else +#error "I don't know how to free a hostent on your system." +#endif +} + +} // namespace notifier diff --git a/chrome/browser/sync/notifier/base/nethelpers.h b/chrome/browser/sync/notifier/base/nethelpers.h new file mode 100644 index 0000000..d2b9fd4 --- /dev/null +++ b/chrome/browser/sync/notifier/base/nethelpers.h @@ -0,0 +1,25 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_SYNC_NOTIFIER_BASE_NETHELPERS_H_ +#define CHROME_BROWSER_SYNC_NOTIFIER_BASE_NETHELPERS_H_ + +#ifdef POSIX +#include <netdb.h> +#include <cstddef> +#elif WIN32 +#include <winsock2.h> +#endif + +namespace notifier { + +hostent* SafeGetHostByName(const char* hostname, hostent* host, + char* buffer, size_t buffer_len, + int* herrno); + +void FreeHostEnt(hostent* host); + +} // namespace notifier + +#endif // CHROME_BROWSER_SYNC_NOTIFIER_BASE_NETHELPERS_H_ diff --git a/chrome/browser/sync/notifier/base/network_status_detector_task.cc b/chrome/browser/sync/notifier/base/network_status_detector_task.cc new file mode 100644 index 0000000..f9acd88 --- /dev/null +++ b/chrome/browser/sync/notifier/base/network_status_detector_task.cc @@ -0,0 +1,30 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/sync/notifier/base/network_status_detector_task.h" + +namespace notifier { + +void NetworkStatusDetectorTask::DetectNetworkState() { + // If the detection has been finished, then just broadcast the current + // state. Otherwise, allow the signal to be sent when the initial + // detection is finished. + if (initial_detection_done_) { + SignalNetworkStateDetected(is_alive_, is_alive_); + } +} + +void NetworkStatusDetectorTask::SetNetworkAlive(bool is_alive) { + bool was_alive = is_alive_; + is_alive_ = is_alive; + + if (!initial_detection_done_ || was_alive != is_alive_) { + initial_detection_done_ = true; + + // Tell everyone about the network state change. + SignalNetworkStateDetected(was_alive, is_alive_); + } +} + +} // namespace notifier diff --git a/chrome/browser/sync/notifier/base/network_status_detector_task.h b/chrome/browser/sync/notifier/base/network_status_detector_task.h new file mode 100644 index 0000000..4cf190e --- /dev/null +++ b/chrome/browser/sync/notifier/base/network_status_detector_task.h @@ -0,0 +1,55 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_SYNC_NOTIFIER_BASE_NETWORK_STATUS_DETECTOR_TASK_H_ +#define CHROME_BROWSER_SYNC_NOTIFIER_BASE_NETWORK_STATUS_DETECTOR_TASK_H_ + +#include "chrome/browser/sync/notifier/base/time.h" +#include "talk/base/sigslot.h" +#include "talk/base/task.h" + +namespace notifier { +class AsyncNetworkAlive; + +// Detects the current network state and any changes to that. +class NetworkStatusDetectorTask : public talk_base::Task, + public sigslot::has_slots<> { + public: + // Create an instance of (a subclass of) this class. + static NetworkStatusDetectorTask* Create(talk_base::Task* parent); + + // Determines the current network state and + // then calls SignalNetworkStateDetected. + void DetectNetworkState(); + + // Fires whenever the network state is detected. + // SignalNetworkStateDetected(was_alive, is_alive); + sigslot::signal2<bool, bool> SignalNetworkStateDetected; + + protected: + explicit NetworkStatusDetectorTask(talk_base::Task* parent) + : talk_base::Task(parent), + initial_detection_done_(false), + is_alive_(false) { + } + + virtual ~NetworkStatusDetectorTask() { } + + virtual int ProcessStart() = 0; + + // Stay around until aborted. + virtual int ProcessResponse() { + return STATE_BLOCKED; + } + + void SetNetworkAlive(bool is_alive); + + private: + bool initial_detection_done_; + bool is_alive_; + + DISALLOW_COPY_AND_ASSIGN(NetworkStatusDetectorTask); +}; +} // namespace notifier +#endif // CHROME_BROWSER_SYNC_NOTIFIER_BASE_NETWORK_STATUS_DETECTOR_TASK_H_ diff --git a/chrome/browser/sync/notifier/base/network_status_detector_task_mt.cc b/chrome/browser/sync/notifier/base/network_status_detector_task_mt.cc new file mode 100644 index 0000000..d4e406c --- /dev/null +++ b/chrome/browser/sync/notifier/base/network_status_detector_task_mt.cc @@ -0,0 +1,48 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/sync/notifier/base/network_status_detector_task_mt.h" + +#include "chrome/browser/sync/notifier/base/async_network_alive.h" +#include "chrome/browser/sync/notifier/base/signal_thread_task.h" + +#include "talk/base/common.h" + +namespace notifier { + +void NetworkStatusDetectorTaskMT::OnNetworkAliveDone( + AsyncNetworkAlive* network_alive) { + ASSERT(network_alive); + SetNetworkAlive(network_alive->alive()); + // If we got an error from detecting the network alive state, + // then stop retrying the detection. + if (network_alive->error()) { + return; + } + StartAsyncDetection(network_alive->ReleaseInfo()); +} + +void NetworkStatusDetectorTaskMT::StartAsyncDetection( + PlatformNetworkInfo* previous_info) { + // Use the AsyncNetworkAlive to determine the network state (and + // changes in the network state). + AsyncNetworkAlive* network_alive = AsyncNetworkAlive::Create(); + + if (previous_info) { + network_alive->SetWaitForNetworkChange(previous_info); + } + SignalThreadTask<AsyncNetworkAlive>* task = + new SignalThreadTask<AsyncNetworkAlive>(this, &network_alive); + task->SignalWorkDone.connect( + this, &NetworkStatusDetectorTaskMT::OnNetworkAliveDone); + task->Start(); +} + +NetworkStatusDetectorTask* NetworkStatusDetectorTask::Create( + talk_base::Task* parent) { + ASSERT(parent); + return new NetworkStatusDetectorTaskMT(parent); +} + +} // namespace notifier diff --git a/chrome/browser/sync/notifier/base/network_status_detector_task_mt.h b/chrome/browser/sync/notifier/base/network_status_detector_task_mt.h new file mode 100644 index 0000000..e1812f2 --- /dev/null +++ b/chrome/browser/sync/notifier/base/network_status_detector_task_mt.h @@ -0,0 +1,34 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_SYNC_NOTIFIER_BASE_NETWORK_STATUS_DETECTOR_TASK_MT_H_ +#define CHROME_BROWSER_SYNC_NOTIFIER_BASE_NETWORK_STATUS_DETECTOR_TASK_MT_H_ + +#include "chrome/browser/sync/notifier/base/network_status_detector_task.h" + +namespace notifier { + +class AsyncNetworkAlive; +class PlatformNetworkInfo; + +class NetworkStatusDetectorTaskMT : public NetworkStatusDetectorTask { + public: + explicit NetworkStatusDetectorTaskMT(talk_base::Task* parent) + : NetworkStatusDetectorTask(parent) { + } + + protected: + virtual int ProcessStart() { + StartAsyncDetection(NULL); + return STATE_RESPONSE; + } + + private: + void OnNetworkAliveDone(AsyncNetworkAlive* network_alive); + void StartAsyncDetection(PlatformNetworkInfo* network_info); +}; + +} // namespace notifier + +#endif // CHROME_BROWSER_SYNC_NOTIFIER_BASE_NETWORK_STATUS_DETECTOR_TASK_MT_H_ diff --git a/chrome/browser/sync/notifier/base/posix/time_posix.cc b/chrome/browser/sync/notifier/base/posix/time_posix.cc new file mode 100644 index 0000000..849f802 --- /dev/null +++ b/chrome/browser/sync/notifier/base/posix/time_posix.cc @@ -0,0 +1,54 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <assert.h> +#include <sys/time.h> +#include <stdlib.h> +#include <string.h> + +#include "chrome/browser/sync/notifier/base/time.h" + +namespace notifier { + +time64 GetCurrent100NSTime() { + struct timeval tv; + struct timezone tz; + + gettimeofday(&tv, &tz); + + time64 retval = tv.tv_sec * kSecsTo100ns; + retval += tv.tv_usec * kMicrosecsTo100ns; + retval += kStart100NsTimeToEpoch; + return retval; +} + +time64 TmToTime64(const struct tm& tm) { + struct tm tm_temp; + memcpy(&tm_temp, &tm, sizeof(struct tm)); + time_t t = timegm(&tm_temp); + return t * kSecsTo100ns; +} + +bool Time64ToTm(time64 t, struct tm* tm) { + assert(tm != NULL); + time_t secs = t / kSecsTo100ns; + gmtime_r(&secs, tm); + return true; +} + +bool UtcTimeToLocalTime(struct tm* tm) { + assert(tm != NULL); + time_t t = timegm(tm); + localtime_r(&t, tm); + return true; +} + +bool LocalTimeToUtcTime(struct tm* tm) { + assert(tm != NULL); + time_t t = mktime(tm); + gmtime_r(&t, tm); + return true; +} + +} // namespace notifier diff --git a/chrome/browser/sync/notifier/base/signal_thread_task.h b/chrome/browser/sync/notifier/base/signal_thread_task.h new file mode 100644 index 0000000..93059d8 --- /dev/null +++ b/chrome/browser/sync/notifier/base/signal_thread_task.h @@ -0,0 +1,92 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_SYNC_NOTIFIER_BASE_SIGNAL_THREAD_TASK_H_ +#define CHROME_BROWSER_SYNC_NOTIFIER_BASE_SIGNAL_THREAD_TASK_H_ + +#include "talk/base/common.h" +#include "talk/base/signalthread.h" +#include "talk/base/sigslot.h" +#include "talk/base/task.h" + +namespace notifier { + +template<class T> +class SignalThreadTask : public talk_base::Task, + public sigslot::has_slots<> { + public: + // Takes ownership of signal_thread. + SignalThreadTask(talk_base::Task* task_parent, T** signal_thread) + : talk_base::Task(task_parent), + signal_thread_(NULL), + finished_(false) { + SetSignalThread(signal_thread); + } + + virtual ~SignalThreadTask() { + ClearSignalThread(); + } + + virtual void Stop() { + Task::Stop(); + ClearSignalThread(); + } + + virtual int ProcessStart() { + ASSERT(GetState() == talk_base::Task::STATE_START); + signal_thread_->SignalWorkDone.connect( + this, + &SignalThreadTask<T>::OnWorkDone); + signal_thread_->Start(); + return talk_base::Task::STATE_RESPONSE; + } + + int ProcessResponse() { + if (!finished_) { + return talk_base::Task::STATE_BLOCKED; + } + SignalWorkDone(signal_thread_); + ClearSignalThread(); + return talk_base::Task::STATE_DONE; + } + + sigslot::signal1<T*> SignalWorkDone; + + private: + // Takes ownership of signal_thread + void SetSignalThread(T** signal_thread) { + ASSERT(!signal_thread_ && signal_thread && *signal_thread); + // Verify that no one is listening to the signal thread + // for work done. They should be using this class instead. + ASSERT((*signal_thread)->SignalWorkDone.is_empty()); + + signal_thread_ = *signal_thread; + + // Helps callers not to use signal thread after this point + // since this class has taken ownership (and avoid the + // error of doing signal_thread->Start()). + *signal_thread = NULL; + } + + void OnWorkDone(talk_base::SignalThread* signal_thread) { + ASSERT(signal_thread == signal_thread_); + finished_ = true; + Wake(); + } + + void ClearSignalThread() { + if (signal_thread_) { + signal_thread_->Destroy(); + signal_thread_ = NULL; + } + } + + T* signal_thread_; + bool finished_; + DISALLOW_COPY_AND_ASSIGN(SignalThreadTask); +}; + +} // namespace notifier + +#endif // CHROME_BROWSER_SYNC_NOTIFIER_BASE_SIGNAL_THREAD_TASK_H_ diff --git a/chrome/browser/sync/notifier/base/static_assert.h b/chrome/browser/sync/notifier/base/static_assert.h new file mode 100644 index 0000000..78e6ac3 --- /dev/null +++ b/chrome/browser/sync/notifier/base/static_assert.h @@ -0,0 +1,19 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_SYNC_NOTIFIER_BASE_STATIC_ASSERT_H_ +#define CHROME_BROWSER_SYNC_NOTIFIER_BASE_STATIC_ASSERT_H_ + +template <bool> struct STATIC_ASSERTION_FAILURE; + +template <> struct STATIC_ASSERTION_FAILURE<true> { enum { value = 1 }; }; + +template<int> struct static_assert_test{}; + +#define STATIC_ASSERT(B) \ +typedef static_assert_test<\ + sizeof(STATIC_ASSERTION_FAILURE< (bool)( B ) >)>\ + static_assert_typedef_ ## __LINE__ + +#endif // CHROME_BROWSER_SYNC_NOTIFIER_BASE_STATIC_ASSERT_H_ diff --git a/chrome/browser/sync/notifier/base/string.cc b/chrome/browser/sync/notifier/base/string.cc new file mode 100644 index 0000000..c3ef54d --- /dev/null +++ b/chrome/browser/sync/notifier/base/string.cc @@ -0,0 +1,403 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifdef OS_MACOSX +#include <CoreFoundation/CoreFoundation.h> +#endif + +#include <float.h> +#include <string.h> + +#include "base/format_macros.h" +#include "base/string_util.h" +#include "chrome/browser/sync/notifier/base/string.h" +#include "talk/base/common.h" +#include "talk/base/logging.h" +#include "talk/base/stringencode.h" + +using base::snprintf; + +namespace notifier { + +std::string HtmlEncode(const std::string& src) { + size_t max_length = src.length() * 6 + 1; + std::string dest; + dest.resize(max_length); + size_t new_size = talk_base::html_encode(&dest[0], max_length, + src.data(), src.length()); + dest.resize(new_size); + return dest; +} + +std::string HtmlDecode(const std::string& src) { + size_t max_length = src.length() + 1; + std::string dest; + dest.resize(max_length); + size_t new_size = talk_base::html_decode(&dest[0], max_length, + src.data(), src.length()); + dest.resize(new_size); + return dest; +} + +std::string UrlEncode(const std::string& src) { + size_t max_length = src.length() * 6 + 1; + std::string dest; + dest.resize(max_length); + size_t new_size = talk_base::url_encode(&dest[0], max_length, + src.data(), src.length()); + dest.resize(new_size); + return dest; +} + +std::string UrlDecode(const std::string& src) { + size_t max_length = src.length() + 1; + std::string dest; + dest.resize(max_length); + size_t new_size = talk_base::url_decode(&dest[0], max_length, + src.data(), src.length()); + dest.resize(new_size); + return dest; +} + +int CharToHexValue(char hex) { + if (hex >= '0' && hex <= '9') { + return hex - '0'; + } else if (hex >= 'A' && hex <= 'F') { + return hex - 'A' + 10; + } else if (hex >= 'a' && hex <= 'f') { + return hex - 'a' + 10; + } else { + return -1; + } +} + +// Template function to convert a string to an int/int64 +// If strict is true, check for the validity and overflow +template<typename T> +bool ParseStringToIntTemplate(const char* str, + T* value, + bool strict, + T min_value) { + ASSERT(str); + ASSERT(value); + + // Skip spaces + while (*str == ' ') { + ++str; + } + + // Process sign + int c = static_cast<int>(*str++); // current char + int possible_sign = c; // save sign indication + if (c == '-' || c == '+') { + c = static_cast<int>(*str++); + } + + // Process numbers + T total = 0; + while (c && (c = CharToDigit(static_cast<char>(c))) != -1) { + // Check for overflow + if (strict && (total < min_value / 10 || + (total == min_value / 10 && + c > ((-(min_value + 10)) % 10)))) { + return false; + } + + // Accumulate digit + // Note that we accumulate in the negative direction so that we will not + // blow away with the largest negative number + total = 10 * total - c; + + // Get next char + c = static_cast<int>(*str++); + } + + // Fail if encountering non-numeric character + if (strict && c == -1) { + return false; + } + + // Negate the number if needed + if (possible_sign == '-') { + *value = total; + } else { + // Check for overflow + if (strict && total == min_value) { + return false; + } + + *value = -total; + } + + return true; +} + +// Convert a string to an int +// If strict is true, check for the validity and overflow +bool ParseStringToInt(const char* str, int* value, bool strict) { + return ParseStringToIntTemplate<int>(str, value, strict, kint32min); +} + +// Convert a string to an int +// This version does not check for the validity and overflow +int StringToInt(const char* str) { + int value = 0; + ParseStringToInt(str, &value, false); + return value; +} + +// Convert a string to an unsigned int. +// If strict is true, check for the validity and overflow +bool ParseStringToUint(const char* str, uint32* value, bool strict) { + ASSERT(str); + ASSERT(value); + + int64 int64_value; + if (!ParseStringToInt64(str, &int64_value, strict)) { + return false; + } + if (int64_value < 0 || int64_value > kuint32max) { + return false; + } + + *value = static_cast<uint32>(int64_value); + return true; +} + +// Convert a string to an int +// This version does not check for the validity and overflow +uint32 StringToUint(const char* str) { + uint32 value = 0; + ParseStringToUint(str, &value, false); + return value; +} + +// Convert a string to an int64 +// If strict is true, check for the validity and overflow +bool ParseStringToInt64(const char* str, int64* value, bool strict) { + return ParseStringToIntTemplate<int64>(str, value, strict, kint64min); +} + +// Convert a string to an int64 +// This version does not check for the validity and overflow +int64 StringToInt64(const char* str) { + int64 value = 0; + ParseStringToInt64(str, &value, false); + return value; +} + +// Convert a string to a double +// If strict is true, check for the validity and overflow +bool ParseStringToDouble(const char* str, double* value, bool strict) { + ASSERT(str); + ASSERT(value); + + // Skip spaces + while (*str == ' ') { + ++str; + } + + // Process sign + int c = static_cast<int>(*str++); // current char + int sign = c; // save sign indication + if (c == '-' || c == '+') { + c = static_cast<int>(*str++); + } + + // Process numbers before "." + double total = 0.0; + while (c && (c != '.') && (c = CharToDigit(static_cast<char>(c))) != -1) { + // Check for overflow + if (strict && total >= DBL_MAX / 10) { + return false; + } + + // Accumulate digit + total = 10.0 * total + c; + + // Get next char + c = static_cast<int>(*str++); + } + + // Process "." + if (c == '.') { + c = static_cast<int>(*str++); + } else { + // Fail if encountering non-numeric character + if (strict && c == -1) { + return false; + } + } + + // Process numbers after "." + double power = 1.0; + while ((c = CharToDigit(static_cast<char>(c))) != -1) { + // Check for overflow + if (strict && total >= DBL_MAX / 10) { + return false; + } + + // Accumulate digit + total = 10.0 * total + c; + power *= 10.0; + + // Get next char + c = static_cast<int>(*str++); + } + + // Get the final number + *value = total / power; + if (sign == '-') { + *value = -(*value); + } + + return true; +} + +// Convert a string to a double +// This version does not check for the validity and overflow +double StringToDouble(const char* str) { + double value = 0; + ParseStringToDouble(str, &value, false); + return value; +} + +// Convert a float to a string +std::string FloatToString(float f) { + char buf[80]; + snprintf(buf, sizeof(buf), "%f", f); + return std::string(buf); +} + +std::string DoubleToString(double d) { + char buf[160]; + snprintf(buf, sizeof(buf), "%.17g", d); + return std::string(buf); +} + +std::string UIntToString(uint32 i) { + char buf[80]; + snprintf(buf, sizeof(buf), "%lu", i); + return std::string(buf); +} + +// Convert an int to a string +std::string IntToString(int i) { + char buf[80]; + snprintf(buf, sizeof(buf), "%d", i); + return std::string(buf); +} + +// Convert an int64 to a string +std::string Int64ToString(int64 i64) { + char buf[80]; + snprintf(buf, sizeof(buf), "%" PRId64 "d", i64); + return std::string(buf); +} + +std::string UInt64ToString(uint64 i64) { + char buf[80]; + snprintf(buf, sizeof(buf), "%" PRId64 "u", i64); + return std::string(buf); +} + +std::string Int64ToHexString(int64 i64) { + char buf[80]; + snprintf(buf, sizeof(buf), "%" PRId64 "x", i64); + return std::string(buf); +} + +// Parse a single "delim" delimited string from "*source" +// Modify *source to point after the delimiter. +// If no delimiter is present after the string, set *source to NULL. +// +// Mainly a stringified wrapper around strpbrk() +std::string SplitOneStringToken(const char** source, const char* delim) { + ASSERT(source); + ASSERT(delim); + + if (!*source) { + return std::string(); + } + const char* begin = *source; + *source = strpbrk(*source, delim); + if (*source) { + return std::string(begin, (*source)++); + } else { + return std::string(begin); + } +} + +std::string LowerWithUnderToPascalCase(const char* lower_with_under) { + ASSERT(lower_with_under); + + std::string pascal_case; + bool make_upper = true; + for (; *lower_with_under != '\0'; lower_with_under++) { + char current_char = *lower_with_under; + if (current_char == '_') { + ASSERT(!make_upper); + make_upper = true; + continue; + } + if (make_upper) { + current_char = toupper(current_char); + make_upper = false; + } + pascal_case.append(1, current_char); + } + return pascal_case; +} + +std::string PascalCaseToLowerWithUnder(const char* pascal_case) { + ASSERT(pascal_case); + + std::string lower_with_under; + bool previous_was_upper = true; + for(; *pascal_case != '\0'; pascal_case++) { + char current_char = *pascal_case; + if (isupper(current_char)) { + // DNSName should be dns_name + if ((islower(pascal_case[1]) && !lower_with_under.empty()) || + !previous_was_upper) { + lower_with_under.append(1, '_'); + } + current_char = tolower(current_char); + } else if (previous_was_upper) { + previous_was_upper = false; + } + lower_with_under.append(1, current_char); + } + return lower_with_under; +} +void StringReplace(std::string* s, + const char* old_sub, + const char* new_sub, + bool replace_all) { + ASSERT(s); + + // If old_sub is empty, nothing to do + if (!old_sub || !*old_sub) { + return; + } + + int old_sub_size = strlen(old_sub); + std::string res; + std::string::size_type start_pos = 0; + + do { + std::string::size_type pos = s->find(old_sub, start_pos); + if (pos == std::string::npos) { + break; + } + res.append(*s, start_pos, pos - start_pos); + res.append(new_sub); + start_pos = pos + old_sub_size; // start searching again after the "old" + } while (replace_all); + res.append(*s, start_pos, s->length() - start_pos); + + *s = res; +} + +} // namespace notifier diff --git a/chrome/browser/sync/notifier/base/string.h b/chrome/browser/sync/notifier/base/string.h new file mode 100644 index 0000000..725cc66 --- /dev/null +++ b/chrome/browser/sync/notifier/base/string.h @@ -0,0 +1,381 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_SYNC_NOTIFIER_BASE_STRING_H_ +#define CHROME_BROWSER_SYNC_NOTIFIER_BASE_STRING_H_ + +#ifdef COMPILER_MSVC +#include <xhash> +#elif defined(__GNUC__) +#include <ext/hash_map> +#endif + +#include <ctype.h> +#include <string> + +#include "chrome/browser/sync/notifier/base/fastalloc.h" +#include "talk/base/basictypes.h" + +namespace notifier { + +// Does html encoding of strings. +std::string HtmlEncode(const std::string& src); + +// Does html decoding of strings. +std::string HtmlDecode(const std::string& src); + +// Does utl encoding of strings. +std::string UrlEncode(const std::string& src); + +// Does url decoding of strings. +std::string UrlDecode(const std::string& src); + +// Convert a character to a digit +// if the character is not a digit return -1 (same as CRT) +inline int CharToDigit(char c) { + return ((c) >= '0' && (c) <= '9' ? (c) - '0' : -1); +} + +int CharToHexValue(char hex); + +// ---------------------------------------------------------------------- +// ParseStringToInt() +// ParseStringToUint() +// ParseStringToInt64() +// ParseStringToDouble() +// Convert a string to an int/int64/double +// If strict is true, check for the validity and overflow +// ---------------------------------------------------------------------- + +bool ParseStringToInt(const char* str, int* value, bool strict); + +bool ParseStringToUint(const char* str, uint32* value, bool strict); + +bool ParseStringToInt64(const char* str, int64* value, bool strict); + +bool ParseStringToDouble(const char* str, double* value, bool strict); + +// ---------------------------------------------------------------------- +// StringToInt() +// StringToUint() +// StringToInt64() +// StringToDouble() +// Convert a string to an int/int64/double +// Note that these functions do not check for the validity or overflow +// ---------------------------------------------------------------------- + +int StringToInt(const char* str); + +uint32 StringToUint(const char* str); + +int64 StringToInt64(const char* str); + +double StringToDouble(const char* str); + +// ---------------------------------------------------------------------- +// FloatToString() +// DoubleToString() +// IntToString() +// UIntToString() +// Int64ToString() +// UInt64ToString() +// Convert various types to their string representation. These +// all do the obvious, trivial thing. +// ---------------------------------------------------------------------- + +std::string FloatToString(float f); +std::string DoubleToString(double d); + +std::string IntToString(int i); +std::string UIntToString(uint32 i); + +std::string Int64ToString(int64 i64); +std::string UInt64ToString(uint64 i64); + +std::string Int64ToHexString(int64 i64); + +// ---------------------------------------------------------------------- +// StringStartsWith() +// StringEndsWith() +// Check if a string starts or ends with a pattern +// ---------------------------------------------------------------------- + +inline bool StringStartsWith(const std::string& s, const char* p) { + return s.find(p) == 0; +} + +inline bool StringEndsWith(const std::string& s, const char* p) { + return s.rfind(p) == (s.length() - strlen(p)); +} + +// ---------------------------------------------------------------------- +// MakeStringEndWith() +// If the string does not end with a pattern, make it end with it +// ---------------------------------------------------------------------- + +inline std::string MakeStringEndWith(const std::string& s, const char* p) { + if (StringEndsWith(s, p)) { + return s; + } else { + std::string ns(s); + ns += p; + return ns; + } +} + +// Convert a lower_case_string to LowerCaseString +std::string LowerWithUnderToPascalCase(const char* lower_with_under); + +// Convert a PascalCaseString to pascal_case_string +std::string PascalCaseToLowerWithUnder(const char* pascal_case); + +// ---------------------------------------------------------------------- +// LowerString() +// LowerStringToBuf() +// Convert the characters in "s" to lowercase. +// Changes contents of "s". LowerStringToBuf copies at most +// "n" characters (including the terminating '\0') from "s" +// to another buffer. +// ---------------------------------------------------------------------- + +inline void LowerString(char* s) { + for (; *s; ++s) { + *s = tolower(*s); + } +} + +inline void LowerString(std::string* s) { + std::string::iterator end = s->end(); + for (std::string::iterator i = s->begin(); i != end; ++i) { + *i = tolower(*i); + } +} + +inline void LowerStringToBuf(const char* s, char* buf, int n) { + for (int i = 0; i < n - 1; ++i) { + char c = s[i]; + buf[i] = tolower(c); + if (c == '\0') { + return; + } + } + buf[n - 1] = '\0'; +} + +// ---------------------------------------------------------------------- +// UpperString() +// UpperStringToBuf() +// Convert the characters in "s" to uppercase. +// UpperString changes "s". UpperStringToBuf copies at most +// "n" characters (including the terminating '\0') from "s" +// to another buffer. +// ---------------------------------------------------------------------- + +inline void UpperString(char* s) { + for (; *s; ++s) { + *s = toupper(*s); + } +} + +inline void UpperString(std::string* s) { + for (std::string::iterator iter = s->begin(); iter != s->end(); ++iter) { + *iter = toupper(*iter); + } +} + +inline void UpperStringToBuf(const char* s, char* buf, int n) { + for (int i = 0; i < n - 1; ++i) { + char c = s[i]; + buf[i] = toupper(c); + if (c == '\0') { + return; + } + } + buf[n - 1] = '\0'; +} + +// ---------------------------------------------------------------------- +// TrimStringLeft +// Removes any occurrences of the characters in 'remove' from the start +// of the string. Returns the number of chars trimmed. +// ---------------------------------------------------------------------- +inline int TrimStringLeft(std::string* s, const char* remove) { + int i = 0; + for (; i < static_cast<int>(s->size()) && strchr(remove, (*s)[i]); ++i); + if (i > 0) s->erase(0, i); + return i; +} + +// ---------------------------------------------------------------------- +// TrimStringRight +// Removes any occurrences of the characters in 'remove' from the end +// of the string. Returns the number of chars trimmed. +// ---------------------------------------------------------------------- +inline int TrimStringRight(std::string* s, const char* remove) { + int size = static_cast<int>(s->size()); + int i = size; + for (; i > 0 && strchr(remove, (*s)[i - 1]); --i); + if (i < size) { + s->erase(i); + } + return size - i; +} + +// ---------------------------------------------------------------------- +// TrimString +// Removes any occurrences of the characters in 'remove' from either +// end of the string. +// ---------------------------------------------------------------------- +inline int TrimString(std::string* s, const char* remove) { + return TrimStringRight(s, remove) + TrimStringLeft(s, remove); +} + +// ---------------------------------------------------------------------- +// StringReplace() +// Replace the "old" pattern with the "new" pattern in a string. If +// replace_all is false, it only replaces the first instance of "old." +// ---------------------------------------------------------------------- + +void StringReplace(std::string* s, + const char* old_sub, + const char* new_sub, + bool replace_all); + +inline size_t HashString(const std::string &value) { +#ifdef COMPILER_MSVC + return stdext::hash_value(value); +#elif defined(__GNUC__) + __gnu_cxx::hash<const char*> h; + return h(value.c_str()); +#else + // Compile time error because we don't return a value +#endif +} + +// ---------------------------------------------------------------------- +// SplitOneStringToken() +// Parse a single "delim" delimited string from "*source" +// Modify *source to point after the delimiter. +// If no delimiter is present after the string, set *source to NULL. +// +// If the start of *source is a delimiter, return an empty string. +// If *source is NULL, return an empty string. +// ---------------------------------------------------------------------- +std::string SplitOneStringToken(const char** source, const char* delim); + +//---------------------------------------------------------------------- +// CharTraits provides wrappers with common function names for char/wchar_t +// specific CRT functions +//---------------------------------------------------------------------- + +template <class CharT> struct CharTraits { +}; + +template <> +struct CharTraits<char> { + static inline size_t length(const char* s) { + return strlen(s); + } + static inline bool copy(char* dst, size_t dst_size, const char* s) { + if (s == NULL || dst == NULL) + return false; + else + return copy_num(dst, dst_size, s, strlen(s)); + } + static inline bool copy_num(char* dst, size_t dst_size, const char* s, + size_t s_len) { + if (dst_size < (s_len + 1)) + return false; + memcpy(dst, s, s_len); + dst[s_len] = '\0'; + return true; + } +}; + +template <> +struct CharTraits<wchar_t> { + static inline size_t length(const wchar_t* s) { + return wcslen(s); + } + static inline bool copy(wchar_t* dst, size_t dst_size, const wchar_t* s) { + if (s == NULL || dst == NULL) + return false; + else + return copy_num(dst, dst_size, s, wcslen(s)); + } + static inline bool copy_num(wchar_t* dst, size_t dst_size, const wchar_t* s, + size_t s_len) { + if (dst_size < (s_len + 1)) { + return false; + } + memcpy(dst, s, s_len * sizeof(wchar_t)); + dst[s_len] = '\0'; + return true; + } +}; + +//---------------------------------------------------------------------- +// This class manages a fixed-size, null-terminated string buffer. It is +// meant to be allocated on the stack, and it makes no use of the heap +// internally. In most cases you'll just want to use a std::(w)string, but +// when you need to avoid the heap, you can use this class instead. +// +// Methods are provided to read the null-terminated buffer and to append +// data to the buffer, and once the buffer fills-up, it simply discards any +// extra append calls. +//---------------------------------------------------------------------- + +template <class CharT, int MaxSize> +class FixedString { + public: + typedef CharTraits<CharT> char_traits; + + FixedString() : index_(0), truncated_(false) { + buf_[0] = CharT(0); + } + + ~FixedString() { + memset(buf_, 0xCC, sizeof(buf_)); + } + + // Returns true if the Append ever failed. + bool was_truncated() const { return truncated_; } + + // Returns the number of characters in the string, excluding the null + // terminator. + size_t size() const { return index_; } + + // Returns the null-terminated string. + const CharT* get() const { return buf_; } + CharT* get() { return buf_; } + + // Append an array of characters. The operation is bounds checked, and if + // there is insufficient room, then the was_truncated() flag is set to true. + void Append(const CharT* s, size_t n) { + if (char_traits::copy_num(buf_ + index_, arraysize(buf_) - index_, s, n)) { + index_ += n; + } else { + truncated_ = true; + } + } + + // Append a null-terminated string. + void Append(const CharT* s) { + Append(s, char_traits::length(s)); + } + + // Append a single character. + void Append(CharT c) { + Append(&c, 1); + } + + private: + CharT buf_[MaxSize]; + size_t index_; + bool truncated_; +}; + +} // namespace notifier + +#endif // CHROME_BROWSER_SYNC_NOTIFIER_BASE_STRING_H_ diff --git a/chrome/browser/sync/notifier/base/string_unittest.cc b/chrome/browser/sync/notifier/base/string_unittest.cc new file mode 100644 index 0000000..954315a --- /dev/null +++ b/chrome/browser/sync/notifier/base/string_unittest.cc @@ -0,0 +1,362 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/sync/notifier/base/string.h" +#include "notifier/testing/notifier/unittest.h" + +namespace notifier { + +TEST_NOTIFIER_F(StringTest); + +TEST_F(StringTest, StringToInt) { + ASSERT_EQ(StringToInt("625"), 625); + ASSERT_EQ(StringToInt("6"), 6); + ASSERT_EQ(StringToInt("0"), 0); + ASSERT_EQ(StringToInt(" 122"), 122); + ASSERT_EQ(StringToInt("a"), 0); + ASSERT_EQ(StringToInt(" a"), 0); + ASSERT_EQ(StringToInt("2147483647"), 2147483647); + ASSERT_EQ(StringToInt("-2147483648"), + static_cast<int>(0x80000000)); // Hex constant avoids gcc warning. + + int value = 0; + ASSERT_FALSE(ParseStringToInt("62.5", &value, true)); + ASSERT_FALSE(ParseStringToInt("625e", &value, true)); + ASSERT_FALSE(ParseStringToInt("2147483648", &value, true)); + ASSERT_FALSE(ParseStringToInt("-2147483649", &value, true)); + ASSERT_FALSE(ParseStringToInt("-4857004031", &value, true)); +} + +TEST_F(StringTest, StringToUint) { + ASSERT_EQ(StringToUint("625"), 625); + ASSERT_EQ(StringToUint("6"), 6); + ASSERT_EQ(StringToUint("0"), 0); + ASSERT_EQ(StringToUint(" 122"), 122); + ASSERT_EQ(StringToUint("a"), 0); + ASSERT_EQ(StringToUint(" a"), 0); + ASSERT_EQ(StringToUint("4294967295"), static_cast<uint32>(0xffffffff)); + + uint32 value = 0; + ASSERT_FALSE(ParseStringToUint("62.5", &value, true)); + ASSERT_FALSE(ParseStringToUint("625e", &value, true)); + ASSERT_FALSE(ParseStringToUint("4294967296", &value, true)); + ASSERT_FALSE(ParseStringToUint("-1", &value, true)); +} + +TEST_F(StringTest, StringToInt64) { + ASSERT_EQ(StringToInt64("119600064000000000"), + INT64_C(119600064000000000)); + ASSERT_EQ(StringToInt64(" 119600064000000000"), + INT64_C(119600064000000000)); + ASSERT_EQ(StringToInt64("625"), 625); + ASSERT_EQ(StringToInt64("6"), 6); + ASSERT_EQ(StringToInt64("0"), 0); + ASSERT_EQ(StringToInt64(" 122"), 122); + ASSERT_EQ(StringToInt64("a"), 0); + ASSERT_EQ(StringToInt64(" a"), 0); + ASSERT_EQ(StringToInt64("9223372036854775807"), INT64_C(9223372036854775807)); + ASSERT_EQ(StringToInt64("-9223372036854775808I64"), + static_cast<int64>(INT64_C(0x8000000000000000))); + + int64 value = 0; + ASSERT_FALSE(ParseStringToInt64("62.5", &value, true)); + ASSERT_FALSE(ParseStringToInt64("625e", &value, true)); + ASSERT_FALSE(ParseStringToInt64("9223372036854775808", &value, true)); + ASSERT_FALSE(ParseStringToInt64("-9223372036854775809", &value, true)); +} + +TEST_F(StringTest, StringToDouble) { + ASSERT_DOUBLE_EQ(StringToDouble("625"), 625); + ASSERT_DOUBLE_EQ(StringToDouble("-625"), -625); + ASSERT_DOUBLE_EQ(StringToDouble("-6.25"), -6.25); + ASSERT_DOUBLE_EQ(StringToDouble("6.25"), 6.25); + ASSERT_DOUBLE_EQ(StringToDouble("0.00"), 0); + ASSERT_DOUBLE_EQ(StringToDouble(" 55.1"), 55.1); + ASSERT_DOUBLE_EQ(StringToDouble(" 55.001"), 55.001); + ASSERT_DOUBLE_EQ(StringToDouble(" 1.001"), 1.001); + + double value = 0.0; + ASSERT_FALSE(ParseStringToDouble("62*5", &value, true)); +} + +TEST_F(StringTest, Int64ToHexString) { + ASSERT_STREQ("1a8e79fe1d58000", + Int64ToHexString(INT64_C(119600064000000000)).c_str()); + ASSERT_STREQ("271", Int64ToHexString(625).c_str()); + ASSERT_STREQ("0", Int64ToHexString(0).c_str()); +} + +TEST_F(StringTest, StringStartsWith) { + { std::string s(""); ASSERT_TRUE(StringStartsWith(s, "")); } + { std::string s("abc"); ASSERT_TRUE(StringStartsWith(s, "ab")); } + { std::string s("abc"); ASSERT_FALSE(StringStartsWith(s, "bc")); } +} + +TEST_F(StringTest, StringEndsWith) { + { std::string s(""); ASSERT_TRUE(StringEndsWith(s, "")); } + { std::string s("abc"); ASSERT_TRUE(StringEndsWith(s, "bc")); } + { std::string s("abc"); ASSERT_FALSE(StringEndsWith(s, "ab")); } +} + +TEST_F(StringTest, MakeStringEndWith) { + { + std::string s(""); + std::string t(MakeStringEndWith(s, "")); + ASSERT_STREQ(t.c_str(), ""); + } + { + std::string s("abc"); + std::string t(MakeStringEndWith(s, "def")); + ASSERT_STREQ(t.c_str(), "abcdef"); + } + { + std::string s("abc"); + std::string t(MakeStringEndWith(s, "bc")); + ASSERT_STREQ(t.c_str(), "abc"); + } +} + +TEST_F(StringTest, LowerString) { + { std::string s(""); LowerString(&s); ASSERT_STREQ(s.c_str(), ""); } + { std::string s("a"); LowerString(&s); ASSERT_STREQ(s.c_str(), "a"); } + { std::string s("A"); LowerString(&s); ASSERT_STREQ(s.c_str(), "a"); } + { std::string s("abc"); LowerString(&s); ASSERT_STREQ(s.c_str(), "abc"); } + { std::string s("ABC"); LowerString(&s); ASSERT_STREQ(s.c_str(), "abc"); } +} + +TEST_F(StringTest, UpperString) { + { std::string s(""); UpperString(&s); ASSERT_STREQ(s.c_str(), ""); } + { std::string s("A"); UpperString(&s); ASSERT_STREQ(s.c_str(), "A"); } + { std::string s("a"); UpperString(&s); ASSERT_STREQ(s.c_str(), "A"); } + { std::string s("ABC"); UpperString(&s); ASSERT_STREQ(s.c_str(), "ABC"); } + { std::string s("abc"); UpperString(&s); ASSERT_STREQ(s.c_str(), "ABC"); } +} + +TEST_F(StringTest, TrimString) { + const char* white = " \n\t"; + std::string s, c; + + // TrimStringLeft + s = ""; // empty + c = ""; + ASSERT_EQ(TrimStringLeft(&s, white), 0); + ASSERT_STREQ(s.c_str(), c.c_str()); + + s = " \n\t"; // all bad + c = ""; + ASSERT_EQ(TrimStringLeft(&s, white), 3); + ASSERT_STREQ(s.c_str(), c.c_str()); + + s = "dog"; // nothing bad + c = "dog"; + ASSERT_EQ(TrimStringLeft(&s, white), 0); + ASSERT_STREQ(s.c_str(), c.c_str()); + + s = " dog "; // some bad + c = "dog "; + ASSERT_EQ(TrimStringLeft(&s, white), 1); + ASSERT_STREQ(s.c_str(), c.c_str()); + + s = " \n\t\t I love my little dog \n\t "; + c = "I love my little dog \n\t "; + ASSERT_EQ(TrimStringLeft(&s, white), 5); + ASSERT_STREQ(s.c_str(), c.c_str()); + + // TrimStringRight + s = ""; + c = ""; + ASSERT_EQ(TrimStringRight(&s, white), 0); + ASSERT_STREQ(s.c_str(), c.c_str()); + + s = " \n\t"; + c = ""; + ASSERT_EQ(TrimStringRight(&s, white), 3); + ASSERT_STREQ(s.c_str(), c.c_str()); + + s = "dog"; + c = "dog"; + ASSERT_EQ(TrimStringRight(&s, white), 0); + ASSERT_STREQ(s.c_str(), c.c_str()); + + s = " dog "; + c = " dog"; + ASSERT_EQ(TrimStringRight(&s, white), 1); + ASSERT_STREQ(s.c_str(), c.c_str()); + + s = " \n\t\t I love my little dog \n\t "; + c = " \n\t\t I love my little dog"; + ASSERT_EQ(TrimStringRight(&s, white), 4); + ASSERT_STREQ(s.c_str(), c.c_str()); + + // TrimString + s = ""; + c = ""; + ASSERT_EQ(TrimString(&s, white), 0); + ASSERT_STREQ(s.c_str(), c.c_str()); + + s = " \n\t"; + c = ""; + ASSERT_EQ(TrimString(&s, white), 3); + ASSERT_STREQ(s.c_str(), c.c_str()); + + s = "dog"; + c = "dog"; + ASSERT_EQ(TrimString(&s, white), 0); + ASSERT_STREQ(s.c_str(), c.c_str()); + + s = " dog "; + c = "dog"; + ASSERT_EQ(TrimString(&s, white), 2); + ASSERT_STREQ(s.c_str(), c.c_str()); + + s = " \n\t\t I love my little dog \n\t "; + c = "I love my little dog"; + ASSERT_EQ(TrimString(&s, white), 9); + ASSERT_STREQ(s.c_str(), c.c_str()); +} + +TEST_F(StringTest, SplitOneStringToken) { + const char* teststrings[] = { + "alongword", + "alongword ", + "alongword ", + "alongword anotherword", + " alongword", + "", + }; + const char* source = NULL; + + source = teststrings[0]; + ASSERT_STREQ(SplitOneStringToken(&source, " ").c_str(), "alongword"); + ASSERT_STREQ(source, NULL); + + source = teststrings[1]; + ASSERT_STREQ(SplitOneStringToken(&source, " ").c_str(), "alongword"); + ASSERT_STREQ(source, teststrings[1] + strlen("alongword") + 1); + + source = teststrings[2]; + ASSERT_STREQ(SplitOneStringToken(&source, " ").c_str(), "alongword"); + ASSERT_STREQ(source, teststrings[2] + strlen("alongword") + 1); + + source = teststrings[3]; + ASSERT_STREQ(SplitOneStringToken(&source, " ").c_str(), "alongword"); + ASSERT_STREQ(source, teststrings[3] + strlen("alongword") + 1); + + source = teststrings[4]; + ASSERT_STREQ(SplitOneStringToken(&source, " ").c_str(), ""); + ASSERT_STREQ(source, teststrings[4] + 1); + + source = teststrings[5]; + ASSERT_STREQ(SplitOneStringToken(&source, " ").c_str(), ""); + ASSERT_STREQ(source, NULL); +} + +TEST_F(StringTest, FixedString) { + // Test basic operation. + const wchar_t kData[] = L"hello world"; + FixedString<wchar_t, 40> buf; + + buf.Append(kData); + EXPECT_EQ(arraysize(kData)-1, buf.size()); + EXPECT_EQ(0, wcscmp(kData, buf.get())); + + buf.Append(' '); + buf.Append(kData); + const wchar_t kExpected[] = L"hello world hello world"; + EXPECT_EQ(arraysize(kExpected)-1, buf.size()); + EXPECT_EQ(0, wcscmp(kExpected, buf.get())); + EXPECT_EQ(false, buf.was_truncated()); + + // Test overflow. + FixedString<wchar_t, 5> buf2; + buf2.Append(L"hello world"); + EXPECT_EQ(static_cast<size_t>(0), buf2.size()); + EXPECT_EQ(0, buf2.get()[0]); + EXPECT_EQ(true, buf2.was_truncated()); +} + +TEST_F(StringTest, LowerToPascalCase) { + EXPECT_STREQ("", LowerWithUnderToPascalCase("").c_str()); + EXPECT_STREQ("A", LowerWithUnderToPascalCase("a").c_str()); + EXPECT_STREQ("TestS", LowerWithUnderToPascalCase("test_s").c_str()); + EXPECT_STREQ("XQ", LowerWithUnderToPascalCase("x_q").c_str()); + EXPECT_STREQ("XQDNS", LowerWithUnderToPascalCase("x_qDNS").c_str()); +} + +TEST_F(StringTest, PascalCaseToLower) { + EXPECT_STREQ("", PascalCaseToLowerWithUnder("").c_str()); + EXPECT_STREQ("a", PascalCaseToLowerWithUnder("A").c_str()); + EXPECT_STREQ("test_s", PascalCaseToLowerWithUnder("TestS").c_str()); + EXPECT_STREQ("xq", PascalCaseToLowerWithUnder("XQ").c_str()); + EXPECT_STREQ("dns_name", PascalCaseToLowerWithUnder("DNSName").c_str()); + EXPECT_STREQ("xqdns", PascalCaseToLowerWithUnder("XQDNS").c_str()); + EXPECT_STREQ("xqdn_sa", PascalCaseToLowerWithUnder("XQDNSa").c_str()); + EXPECT_STREQ("dns1", PascalCaseToLowerWithUnder("DNS1").c_str()); +} + +TEST_F(StringTest, HtmlEncode) { + EXPECT_STREQ("dns", HtmlEncode("dns").c_str()); + EXPECT_STREQ("&", HtmlEncode("&").c_str()); + EXPECT_STREQ("&amp;", HtmlEncode("&").c_str()); + EXPECT_STREQ("<!>", HtmlEncode("<!>").c_str()); +} + +TEST_F(StringTest, HtmlDecode) { + EXPECT_STREQ("dns", HtmlDecode("dns").c_str()); + EXPECT_STREQ("&", HtmlDecode("&").c_str()); + EXPECT_STREQ("&", HtmlDecode("&amp;").c_str()); + EXPECT_STREQ("<!>", HtmlDecode("<!>").c_str()); +} + +TEST_F(StringTest, UrlEncode) { + EXPECT_STREQ("%26", UrlEncode("&").c_str()); + EXPECT_STREQ("%3f%20", UrlEncode("? ").c_str()); + EXPECT_STREQ("as%20dfdsa", UrlEncode("as dfdsa").c_str()); + EXPECT_STREQ("%3c!%3e", UrlEncode("<!>").c_str()); + EXPECT_STREQ("!%23!", UrlEncode("!#!").c_str()); + EXPECT_STREQ("!!", UrlEncode("!!").c_str()); +} + +TEST_F(StringTest, UrlDecode) { + EXPECT_STREQ("&", UrlDecode("%26").c_str()); + EXPECT_STREQ("? ", UrlDecode("%3f%20").c_str()); + EXPECT_STREQ("as dfdsa", UrlDecode("as%20dfdsa").c_str()); + EXPECT_STREQ("<!>", UrlDecode("%3c!%3e").c_str()); + EXPECT_STREQ("&", UrlDecode("&").c_str()); +} + +TEST_F(StringTest, StringReplace) { + // Test StringReplace core functionality. + std::string s = "<attribute name=abcd/>"; + StringReplace(&s, "=", " = ", false); + EXPECT_STREQ(s.c_str(), "<attribute name = abcd/>"); + + // Test for negative case. + s = "<attribute name=abcd/>"; + StringReplace(&s, "-", "=", false); + EXPECT_STREQ(s.c_str(), "<attribute name=abcd/>"); + + // Test StringReplace core functionality with replace_all flag set. + s = "<attribute name==abcd/>"; + StringReplace(&s, "=", " = ", true); + EXPECT_STREQ(s.c_str(), "<attribute name = = abcd/>"); + + // Input is an empty string. + s = ""; + StringReplace(&s, "=", " = ", false); + EXPECT_STREQ(s.c_str(), ""); + + // Input is an empty string and this is a request for repeated + // string replaces. + s = ""; + StringReplace(&s, "=", " = ", true); + EXPECT_STREQ(s.c_str(), ""); + + // Input and string to replace is an empty string. + s = ""; + StringReplace(&s, "", " = ", false); + EXPECT_STREQ(s.c_str(), ""); +} + +} // namespace notifier diff --git a/chrome/browser/sync/notifier/base/task_pump.cc b/chrome/browser/sync/notifier/base/task_pump.cc new file mode 100644 index 0000000..7e99fc1 --- /dev/null +++ b/chrome/browser/sync/notifier/base/task_pump.cc @@ -0,0 +1,42 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/sync/notifier/base/task_pump.h" + +#include "chrome/browser/sync/notifier/base/time.h" +#include "talk/base/common.h" +#include "talk/base/thread.h" + +namespace notifier { + +// Don't add any messages because there are cleared and thrown away. +enum { MSG_WAKE_UP = 1, MSG_TIMED_WAKE_UP }; + +TaskPump::TaskPump() : timeout_change_count_(0), posted_(false) { +} + +void TaskPump::OnMessage(talk_base::Message* msg) { + posted_ = false; + int initial_count = timeout_change_count_; + + // If a task timed out, ensure that it is not blocked, so it will be deleted. + // This may result in a WakeTasks if a task is timed out. + PollTasks(); + + // Run tasks and handle timeouts. + RunTasks(); +} + +void TaskPump::WakeTasks() { + if (!posted_) { + // Do the requested wake up + talk_base::Thread::Current()->Post(this, MSG_WAKE_UP); + posted_ = true; + } +} + +int64 TaskPump::CurrentTime() { + return GetCurrent100NSTime(); +} +} // namespace notifier diff --git a/chrome/browser/sync/notifier/base/task_pump.h b/chrome/browser/sync/notifier/base/task_pump.h new file mode 100644 index 0000000..b6c00ec --- /dev/null +++ b/chrome/browser/sync/notifier/base/task_pump.h @@ -0,0 +1,34 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_SYNC_NOTIFIER_BASE_TASK_PUMP_H_ +#define CHROME_BROWSER_SYNC_NOTIFIER_BASE_TASK_PUMP_H_ + +#include "talk/base/messagequeue.h" +#include "talk/base/taskrunner.h" + +namespace notifier { + +class TaskPump : public talk_base::MessageHandler, + public talk_base::TaskRunner { + public: + TaskPump(); + + // MessageHandler interface. + virtual void OnMessage(talk_base::Message* msg); + + // TaskRunner interface + virtual void WakeTasks(); + virtual int64 CurrentTime(); + + private: + int timeout_change_count_; + bool posted_; + + DISALLOW_COPY_AND_ASSIGN(TaskPump); +}; + +} // namespace notifier + +#endif // CHROME_BROWSER_SYNC_NOTIFIER_BASE_TASK_PUMP_H_ diff --git a/chrome/browser/sync/notifier/base/time.cc b/chrome/browser/sync/notifier/base/time.cc new file mode 100644 index 0000000..ed3c414 --- /dev/null +++ b/chrome/browser/sync/notifier/base/time.cc @@ -0,0 +1,360 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/sync/notifier/base/time.h" + +#include <string> +#include <time.h> + +#include "chrome/browser/sync/notifier/base/string.h" +#include "chrome/browser/sync/notifier/base/utils.h" +#include "talk/base/common.h" +#include "talk/base/logging.h" + +namespace notifier { + +// Get the current time represented in 100NS granularity since epoch +// (Jan 1, 1970) +time64 GetCurrent100NSTimeSinceEpoch() { + return GetCurrent100NSTime() - kStart100NsTimeToEpoch; +} + +char* GetLocalTimeAsString() { + time64 long_time = GetCurrent100NSTime(); + struct tm now; + Time64ToTm(long_time, &now); + char* time_string = asctime(&now); + if (time_string) { + int time_len = strlen(time_string); + if (time_len > 0) { + time_string[time_len - 1] = 0; // trim off terminating \n + } + } + return time_string; +} + +// Parses RFC 822 Date/Time format +// 5. DATE AND TIME SPECIFICATION +// 5.1. SYNTAX +// +// date-time = [ day "," ] date time ; dd mm yy +// ; hh:mm:ss zzz +// day = "Mon" / "Tue" / "Wed" / "Thu" +// / "Fri" / "Sat" / "Sun" +// +// date = 1*2DIGIT month 2DIGIT ; day month year +// ; e.g. 20 Jun 82 +// +// month = "Jan" / "Feb" / "Mar" / "Apr" +// / "May" / "Jun" / "Jul" / "Aug" +// / "Sep" / "Oct" / "Nov" / "Dec" +// +// time = hour zone ; ANSI and Military +// +// hour = 2DIGIT ":" 2DIGIT [":" 2DIGIT] +// ; 00:00:00 - 23:59:59 +// +// zone = "UT" / "GMT" ; Universal Time +// ; North American : UT +// / "EST" / "EDT" ; Eastern: - 5/ - 4 +// / "CST" / "CDT" ; Central: - 6/ - 5 +// / "MST" / "MDT" ; Mountain: - 7/ - 6 +// / "PST" / "PDT" ; Pacific: - 8/ - 7 +// / 1ALPHA ; Military: Z = UT; +// ; A:-1; (J not used) +// ; M:-12; N:+1; Y:+12 +// / ( ("+" / "-") 4DIGIT ) ; Local differential +// ; hours+min. (HHMM) +// Return local time if ret_local_time == true, return UTC time otherwise +const int kNumOfDays = 7; +const int kNumOfMonth = 12; +// Note: RFC822 does not include '-' as a separator, but Http Cookies use +// it in the date field, like this: Wdy, DD-Mon-YYYY HH:MM:SS GMT +// This differs from RFC822 only by those dashes. It is legacy quirk from +// old Netscape cookie specification. So it makes sense to expand this +// parser rather then add another one. +// See http://wp.netscape.com/newsref/std/cookie_spec.html +const char kRFC822_DateDelimiters[] = " ,:-"; + +const char kRFC822_TimeDelimiter[] = ": "; +const char* kRFC822_Day[] = { + "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" +}; +const char* kRFC822_Month[] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" +}; + +struct TimeZoneInfo { + const char* zone_name; + int hour_dif; +}; + +const TimeZoneInfo kRFC822_TimeZone[] = { + { "UT", 0 }, + { "GMT", 0 }, + { "EST", -5 }, + { "EDT", -4 }, + { "CST", -6 }, + { "CDT", -5 }, + { "MST", -7 }, + { "MDT", -6 }, + { "PST", -8 }, + { "PDT", -7 }, + { "A", -1 }, // Military time zones + { "B", -2 }, + { "C", -3 }, + { "D", -4 }, + { "E", -5 }, + { "F", -6 }, + { "G", -7 }, + { "H", -8 }, + { "I", -9 }, + { "K", -10 }, + { "L", -11 }, + { "M", -12 }, + { "N", 1 }, + { "O", 2 }, + { "P", 3 }, + { "Q", 4 }, + { "R", 5 }, + { "S", 6 }, + { "T", 7 }, + { "U", 8 }, + { "V", 9 }, + { "W", 10 }, + { "X", 11 }, + { "Y", 12 }, + { "Z", 0 }, +}; + +bool ParseRFC822DateTime(const char* str, struct tm* time, + bool ret_local_time) { + ASSERT(str && *str); + ASSERT(time); + + std::string str_date(str); + std::string str_token; + const char* str_curr = str_date.c_str(); + + str_token = SplitOneStringToken(&str_curr, kRFC822_DateDelimiters); + if (str_token == "") { + return false; + } + + for (int i = 0; i < kNumOfDays; ++i) { + if (str_token == kRFC822_Day[i]) { + // Skip spaces after ',' + while (*str_curr == ' ' && *str_curr != '\0') { + str_curr++; + } + + str_token = SplitOneStringToken(&str_curr, kRFC822_DateDelimiters); + if (str_token == "") { + return false; + } + break; + } + } + + int day = 0; + if (!ParseStringToInt(str_token.c_str(), &day, true) || day < 0 || day > 31) { + return false; + } + + str_token = SplitOneStringToken(&str_curr, kRFC822_DateDelimiters); + if (str_token == "") { + return false; + } + + int month = -1; + for (int i = 0; i < kNumOfMonth; ++i) { + if (str_token == kRFC822_Month[i]) { + month = i; // month is 0 based number + break; + } + } + if (month == -1) { // month not found + return false; + } + + str_token = SplitOneStringToken(&str_curr, kRFC822_DateDelimiters); + if (str_token == "") { + return false; + } + + int year = 0; + if (!ParseStringToInt(str_token.c_str(), &year, true)) { + return false; + } + if (year < 100) { // two digit year format, convert to 1950 - 2050 range + if (year < 50) { + year += 2000; + } else { + year += 1900; + } + } + + str_token = SplitOneStringToken(&str_curr, kRFC822_TimeDelimiter); + if (str_token == "") { + return false; + } + + int hour = 0; + if (!ParseStringToInt(str_token.c_str(), &hour, true) || + hour < 0 || hour > 23) { + return false; + } + + str_token = SplitOneStringToken(&str_curr, kRFC822_TimeDelimiter); + if (str_token == "") { + return false; + } + + int minute = 0; + if (!ParseStringToInt(str_token.c_str(), &minute, true) || + minute < 0 || minute > 59) { + return false; + } + + str_token = SplitOneStringToken(&str_curr, kRFC822_TimeDelimiter); + if (str_token == "") { + return false; + } + + int second = 0; + // distingushed between XX:XX and XX:XX:XX time formats + if (str_token.size() == 2 && isdigit(str_token[0]) && isdigit(str_token[1])) { + second = 0; + if (!ParseStringToInt(str_token.c_str(), &second, true) || + second < 0 || second > 59) { + return false; + } + + str_token = SplitOneStringToken(&str_curr, kRFC822_TimeDelimiter); + if (str_token == "") { + return false; + } + } + + int bias = 0; + if (str_token[0] == '+' || str_token[0] == '-' || isdigit(str_token[0])) { + // numeric format + int zone = 0; + if (!ParseStringToInt(str_token.c_str(), &zone, true)) { + return false; + } + + // zone is in HHMM format, need to convert to the number of minutes + bias = (zone / 100) * 60 + (zone % 100); + } else { // text format + for (size_t i = 0; i < sizeof(kRFC822_TimeZone) / sizeof(TimeZoneInfo); + ++i) { + if (str_token == kRFC822_TimeZone[i].zone_name) { + bias = kRFC822_TimeZone[i].hour_dif * 60; + break; + } + } + } + + SetZero(*time); + time->tm_year = year - 1900; + time->tm_mon = month; + time->tm_mday = day; + time->tm_hour = hour; + time->tm_min = minute; + time->tm_sec = second; + + time64 time_64 = TmToTime64(*time); + time_64 = time_64 - bias * kMinsTo100ns; + + if (!Time64ToTm(time_64, time)) { + return false; + } + + if (ret_local_time) { + if (!UtcTimeToLocalTime(time)) { + return false; + } + } + + return true; +} + +// Parse a string to time span +// +// A TimeSpan value can be represented as +// [d.]hh:mm:ss +// +// d = days (optional) +// hh = hours as measured on a 24-hour clock +// mm = minutes +// ss = seconds +bool ParseStringToTimeSpan(const char* str, time64* time_span) { + ASSERT(str); + ASSERT(time_span); + + const char kColonDelimitor[] = ":"; + const char kDotDelimitor = '.'; + + std::string str_span(str); + time64 span = 0; + + int idx = str_span.find(kDotDelimitor); + if (idx != -1) { + std::string str_day = str_span.substr(0, idx); + int day = 0; + if (!ParseStringToInt(str_day.c_str(), &day, true) || + day < 0 || day > 365) { + return false; + } + span = day; + + str_span = str_span.substr(idx + 1); + } + + const char* str_curr = str_span.c_str(); + std::string str_token; + + str_token = SplitOneStringToken(&str_curr, kColonDelimitor); + if (str_token == "") { + return false; + } + + int hour = 0; + if (!ParseStringToInt(str_token.c_str(), &hour, true) || + hour < 0 || hour > 23) { + return false; + } + span = span * 24 + hour; + + str_token = SplitOneStringToken(&str_curr, kColonDelimitor); + if (str_token == "") { + return false; + } + + int minute = 0; + if (!ParseStringToInt(str_token.c_str(), &minute, true) || + minute < 0 || minute > 59) { + return false; + } + span = span * 60 + minute; + + str_token = SplitOneStringToken(&str_curr, kColonDelimitor); + if (str_token == "") { + return false; + } + + int second = 0; + if (!ParseStringToInt(str_token.c_str(), &second, true) || + second < 0 || second > 59) { + return false; + } + + *time_span = (span * 60 + second) * kSecsTo100ns; + + return true; +} + +} // namespace notifier diff --git a/chrome/browser/sync/notifier/base/time.h b/chrome/browser/sync/notifier/base/time.h new file mode 100644 index 0000000..9bdb6f6 --- /dev/null +++ b/chrome/browser/sync/notifier/base/time.h @@ -0,0 +1,114 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_SYNC_NOTIFIER_BASE_TIME_H_ +#define CHROME_BROWSER_SYNC_NOTIFIER_BASE_TIME_H_ + +#include <time.h> + +#include "talk/base/basictypes.h" + +typedef uint64 time64; + +#define kMicrosecsTo100ns (static_cast<time64>(10)) +#define kMillisecsTo100ns (static_cast<time64>(10000)) +#define kSecsTo100ns (1000 * kMillisecsTo100ns) +#define kMinsTo100ns (60 * kSecsTo100ns) +#define kHoursTo100ns (60 * kMinsTo100ns) +#define kDaysTo100ns (24 * kHoursTo100ns) +const time64 kMaxTime100ns = UINT64_C(9223372036854775807); + +// Time difference in 100NS granularity between platform-dependent starting +// time and Jan 1, 1970. +#ifdef WIN32 +// On Windows time64 is seconds since Jan 1, 1601. +#define kStart100NsTimeToEpoch (116444736000000000uI64) // Jan 1, 1970 in time64 +#else +// On Unix time64 is seconds since Jan 1, 1970. +#define kStart100NsTimeToEpoch (0) // Jan 1, 1970 in time64 +#endif + +// Time difference in 100NS granularity between platform-dependent starting +// time and Jan 1, 1980. +#define kStart100NsTimeTo1980 \ + kStart100NsTimeToEpoch + UINT64_C(3155328000000000) + +#define kTimeGranularity (kDaysTo100ns) + +namespace notifier { + +// Get the current time represented in 100NS granularity +// Different platform might return the value since different starting time. +// Win32 platform returns the value since Jan 1, 1601. +time64 GetCurrent100NSTime(); + +// Get the current time represented in 100NS granularity since epoch +// (Jan 1, 1970). +time64 GetCurrent100NSTimeSinceEpoch(); + +// Convert from struct tm to time64. +time64 TmToTime64(const struct tm& tm); + +// Convert from time64 to struct tm. +bool Time64ToTm(time64 t, struct tm* tm); + +// Convert from UTC time to local time. +bool UtcTimeToLocalTime(struct tm* tm); + +// Convert from local time to UTC time. +bool LocalTimeToUtcTime(struct tm* tm); + +// Returns the local time as a string suitable for logging +// Note: This is *not* threadsafe, so only call it from the main thread. +char* GetLocalTimeAsString(); + +// Parses RFC 822 Date/Time format +// 5. DATE AND TIME SPECIFICATION +// 5.1. SYNTAX +// +// date-time = [ day "," ] date time ; dd mm yy +// ; hh:mm:ss zzz +// day = "Mon" / "Tue" / "Wed" / "Thu" +// / "Fri" / "Sat" / "Sun" +// +// date = 1*2DIGIT month 2DIGIT ; day month year +// ; e.g. 20 Jun 82 +// +// month = "Jan" / "Feb" / "Mar" / "Apr" +// / "May" / "Jun" / "Jul" / "Aug" +// / "Sep" / "Oct" / "Nov" / "Dec" +// +// time = hour zone ; ANSI and Military +// +// hour = 2DIGIT ":" 2DIGIT [":" 2DIGIT] +// ; 00:00:00 - 23:59:59 +// +// zone = "UT" / "GMT" ; Universal Time +// ; North American : UT +// / "EST" / "EDT" ; Eastern: - 5/ - 4 +// / "CST" / "CDT" ; Central: - 6/ - 5 +// / "MST" / "MDT" ; Mountain: - 7/ - 6 +// / "PST" / "PDT" ; Pacific: - 8/ - 7 +// / 1ALPHA ; Military: Z = UT; +// ; A:-1; (J not used) +// ; M:-12; N:+1; Y:+12 +// / ( ("+" / "-") 4DIGIT ) ; Local differential +// ; hours+min. (HHMM) +// Return local time if ret_local_time == true, return UTC time otherwise +bool ParseRFC822DateTime(const char* str, struct tm* time, bool ret_local_time); + +// Parse a string to time span. +// +// A TimeSpan value can be represented as +// [d.]hh:mm:ss +// +// d = days (optional) +// hh = hours as measured on a 24-hour clock +// mm = minutes +// ss = seconds +bool ParseStringToTimeSpan(const char* str, time64* time_span); + +} // namespace notifier + +#endif // CHROME_BROWSER_SYNC_NOTIFIER_BASE_TIME_H_ diff --git a/chrome/browser/sync/notifier/base/time_unittest.cc b/chrome/browser/sync/notifier/base/time_unittest.cc new file mode 100644 index 0000000..0a34b0a --- /dev/null +++ b/chrome/browser/sync/notifier/base/time_unittest.cc @@ -0,0 +1,73 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/sync/notifier/base/time.h" +#include "notifier/testing/notifier/unittest.h" + +namespace notifier { + +TEST_NOTIFIER_F(TimeTest); + +TEST_F(TimeTest, ParseRFC822DateTime) { + struct tm t = {0}; + + EXPECT_TRUE(ParseRFC822DateTime("Mon, 16 May 2005 15:44:18 -0700", + &t, false)); + EXPECT_EQ(t.tm_year, 2005 - 1900); + EXPECT_EQ(t.tm_mon, 4); + EXPECT_EQ(t.tm_mday, 16); + EXPECT_EQ(t.tm_hour, 22); + EXPECT_EQ(t.tm_min, 44); + EXPECT_EQ(t.tm_sec, 18); + + EXPECT_TRUE(ParseRFC822DateTime("Mon, 16 May 2005 15:44:18 -0700", &t, true)); + EXPECT_EQ(t.tm_year, 2005 - 1900); + EXPECT_EQ(t.tm_mon, 4); + EXPECT_EQ(t.tm_mday, 16); + EXPECT_TRUE(t.tm_hour == 15 || t.tm_hour == 14); // daylight saving time + EXPECT_EQ(t.tm_min, 44); + EXPECT_EQ(t.tm_sec , 18); + + EXPECT_TRUE(ParseRFC822DateTime("Tue, 17 May 2005 02:56:18 +0400", + &t, false)); + EXPECT_EQ(t.tm_year, 2005 - 1900); + EXPECT_EQ(t.tm_mon, 4); + EXPECT_EQ(t.tm_mday, 16); + EXPECT_EQ(t.tm_hour, 22); + EXPECT_EQ(t.tm_min, 56); + EXPECT_EQ(t.tm_sec , 18); + + EXPECT_TRUE(ParseRFC822DateTime("Tue, 17 May 2005 02:56:18 +0400", &t, true)); + EXPECT_EQ(t.tm_year, 2005 - 1900); + EXPECT_EQ(t.tm_mon, 4); + EXPECT_EQ(t.tm_mday, 16); + EXPECT_TRUE(t.tm_hour == 15 || t.tm_hour == 14); // daylight saving time + EXPECT_EQ(t.tm_min, 56); + EXPECT_EQ(t.tm_sec, 18); +} + +TEST_F(TimeTest, ParseStringToTimeSpan) { + time64 time_span = 0; + + EXPECT_TRUE(ParseStringToTimeSpan("0:0:4", &time_span)); + EXPECT_EQ(time_span, 4 * kSecsTo100ns); + + EXPECT_TRUE(ParseStringToTimeSpan("0:3:4", &time_span)); + EXPECT_EQ(time_span, (3 * 60 + 4) * kSecsTo100ns); + + EXPECT_TRUE(ParseStringToTimeSpan("2:3:4", &time_span)); + EXPECT_EQ(time_span, (2 * 3600 + 3 * 60 + 4) * kSecsTo100ns); + + EXPECT_TRUE(ParseStringToTimeSpan("1.2:3:4", &time_span)); + EXPECT_EQ(time_span, (1 * 86400 + 2 * 60 * 60 + 3 * 60 + 4) * kSecsTo100ns); + + EXPECT_FALSE(ParseStringToTimeSpan("2:invalid:4", &time_span)); +} + +TEST_F(TimeTest, UseLocalTimeAsString) { + // Just call it to ensure that it doesn't assert. + GetLocalTimeAsString(); +} + +} // namespace notifier diff --git a/chrome/browser/sync/notifier/base/timer.cc b/chrome/browser/sync/notifier/base/timer.cc new file mode 100644 index 0000000..7fa20b4 --- /dev/null +++ b/chrome/browser/sync/notifier/base/timer.cc @@ -0,0 +1,33 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/sync/notifier/base/timer.h" + +namespace notifier { + +Timer::Timer(talk_base::Task* parent, int timeout_seconds, bool repeat) + : Task(parent), + repeat_(repeat) { + + set_timeout_seconds(timeout_seconds); + Start(); + ResumeTimeout(); +} + +Timer::~Timer() { +} + +int Timer::OnTimeout() { + if (!repeat_) { + return STATE_DONE; + } + ResetTimeout(); + return STATE_BLOCKED; +} + +int Timer::ProcessStart() { + return STATE_BLOCKED; +} + +} // namespace notifier diff --git a/chrome/browser/sync/notifier/base/timer.h b/chrome/browser/sync/notifier/base/timer.h new file mode 100644 index 0000000..dd68c73 --- /dev/null +++ b/chrome/browser/sync/notifier/base/timer.h @@ -0,0 +1,40 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_SYNC_NOTIFIER_BASE_TIMER_H_ +#define CHROME_BROWSER_SYNC_NOTIFIER_BASE_TIMER_H_ + +#include "talk/base/task.h" + +namespace notifier { + +class Timer : private talk_base::Task { + public: + Timer(talk_base::Task* parent, int timeout_seconds, bool repeat); + ~Timer(); + + // Call Abort() to stop the timer. + using talk_base::Task::Abort; + + // Call to find out when the timer is set to go off + // Returns int64 + using talk_base::Task::get_timeout_time; + + // Call to set the timeout interval. + using talk_base::Task::set_timeout_seconds; + + using talk_base::Task::SignalTimeout; + + private: + virtual int OnTimeout(); + virtual int ProcessStart(); + + bool repeat_; + + DISALLOW_COPY_AND_ASSIGN(Timer); +}; + +} // namespace notifier + +#endif // CHROME_BROWSER_SYNC_NOTIFIER_BASE_TIMER_H_ diff --git a/chrome/browser/sync/notifier/base/utils.h b/chrome/browser/sync/notifier/base/utils.h new file mode 100644 index 0000000..2105233 --- /dev/null +++ b/chrome/browser/sync/notifier/base/utils.h @@ -0,0 +1,91 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Utility functions + +#ifndef CHROME_BROWSER_SYNC_NOTIFIER_BASE_UTILS_H_ +#define CHROME_BROWSER_SYNC_NOTIFIER_BASE_UTILS_H_ + +#include <map> +#include <string> + +#include "chrome/browser/sync/notifier/base/static_assert.h" + +// return error if the first argument evaluates to false +#define RET_IF_FALSE(x) do { if (!(x)) return false; } while (false) + +// Protocol constants +const char kHttpProto[] = "http://"; +const char kHttpsProto[] = "https://"; + +// Initialize a POD to zero. +// Using this function requires discipline. Don't use for types that have a +// v-table or virtual bases. +template <typename T> +inline void SetZero(T& p) { + // Guard against the easy mistake of + // foo(int *p) { SetZero(p); } instead of + // SetZero(*p); + // which it should be. + STATIC_ASSERT(sizeof(T) != sizeof(void*)); + + // A POD (plain old data) object has one of these data types: + // a fundamental type, union, struct, array, + // or class--with no constructor. PODs don't have virtual functions or + // virtual bases. + + // Test to see if the type has constructors. + union CtorTest { + T t; + int i; + }; + + // TODO(sync) There might be a way to test if the type has virtuals + // For now, if we zero a type with virtuals by mistake, it is going to crash + // predictable at run-time when the virtuals are called. + memset(&p, 0, sizeof(T)); +} + +// Used to delete each element in a vector<T*>/deque<T*> +// (and then empty the sequence). +template <class T> +void CleanupSequence(T* items) { + for (typename T::iterator it(items->begin()); it != items->end(); ++it) { + delete (*it); + } + items->clear(); +} + +// Typically used to clean up values used in a hash_map +// that had Type* as values. +// +// WARNING: This function assumes that T::clear will not access the values +// (or the keys if they are the same as the values). This is true +// for hash_map. +template <class T> +void CleanupMap(T* items) { + // This is likely slower than a for loop, but we have to do it this way. In + // some of the maps we use, deleting it->second causes it->first to be deleted + // as well, and that seems to send the iterator in a tizzy. + typename T::iterator it = items->begin(); + while (it != items->end()) { + items->erase(it->first); + delete it->second; + it = items->begin(); + } +} + +// Get the value of an element in the map with the specified name +template <class T> +void GetMapElement(const std::map<const std::string, const T>& m, + const char* name, + T* value) { + typename std::map<const std::string, const T>::const_iterator iter( + m.find(name)); + if (iter != m.end()) { + *value = iter->second; + } +} + +#endif // CHROME_BROWSER_SYNC_NOTIFIER_BASE_UTILS_H_ diff --git a/chrome/browser/sync/notifier/base/win32/async_network_alive_win32.cc b/chrome/browser/sync/notifier/base/win32/async_network_alive_win32.cc new file mode 100644 index 0000000..b344817 --- /dev/null +++ b/chrome/browser/sync/notifier/base/win32/async_network_alive_win32.cc @@ -0,0 +1,233 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <winsock2.h> + +#include "chrome/browser/sync/notifier/base/async_network_alive.h" +#include "chrome/browser/sync/notifier/base/utils.h" +#include "talk/base/criticalsection.h" +#include "talk/base/logging.h" +#include "talk/base/scoped_ptr.h" +#include "talk/base/common.h" +#include "third_party/smartany/scoped_any.h" + +namespace notifier { +class PlatformNetworkInfo { + public: + PlatformNetworkInfo() : ws_handle_(NULL), event_handle_(NULL) { + } + + ~PlatformNetworkInfo() { + Close(); + } + + void Close() { + talk_base::CritScope crit_scope(&crit_sect_); + if (ws_handle_) { + if (event_handle_) // unblock any waiting for network changes + SetEvent(get(event_handle_)); + // finishes the iteration. + VERIFY(WSALookupServiceEnd(ws_handle_) == 0); + ws_handle_ = NULL; + LOG_F(LS_INFO) << "WSACleanup 1"; + ::WSACleanup(); + } + } + + bool IsAlive(bool* error) { + ASSERT(error); + *error = false; + + // If IsAlive was previously called, we need a new handle. + // Why? If we use the same handle, we only get diffs on what changed + // which isn't what we want. + Close(); + int result = Initialize(); + if (result != 0) { + LOG_F(LS_ERROR) << "failed:" << result; + // Default to alive on error. + *error = true; + return true; + } + + bool alive = false; + + // Retrieve network info and move to next one. In this function, we only + // need to know whether or not there is network connection. + // allocate 256 bytes for name, it should be enough for most cases. + // If the name is longer, it is OK as we will check the code returned and + // set correct network status. + char result_buffer[sizeof(WSAQUERYSET) + 256] = {0}; + bool flush_previous_result = false; + do { + DWORD control_flags = LUP_RETURN_NAME; + if (flush_previous_result) { + control_flags |= LUP_FLUSHPREVIOUS; + } + DWORD length = sizeof(result_buffer); + reinterpret_cast<WSAQUERYSET*>(&result_buffer[0])->dwSize = + sizeof(WSAQUERYSET); + // ws_handle_ may be NULL (if exiting), but the call will simply fail + int result = ::WSALookupServiceNext( + ws_handle_, + control_flags, + &length, + reinterpret_cast<WSAQUERYSET*>(&result_buffer[0])); + + if (result == 0) { + // get at least one connection, return "connected". + alive = true; + } else { + ASSERT(result == SOCKET_ERROR); + result = ::WSAGetLastError(); + if (result == WSA_E_NO_MORE || result == WSAENOMORE) { + break; + } + + // Error code WSAEFAULT means there is a network connection but the + // result_buffer size is too small to contain the results. The + // variable "length" returned from WSALookupServiceNext is the minimum + // number of bytes required. We do not need to retrieve detail info. + // Return "alive" in this case. + if (result == WSAEFAULT) { + alive = true; + flush_previous_result = true; + } else { + LOG_F(LS_WARNING) << "failed:" << result; + *error = true; + break; + } + } + } while (true); + LOG_F(LS_INFO) << "alive: " << alive; + return alive; + } + + bool WaitForChange() { + // IsAlive must be called first. + int junk1 = 0, junk2 = 0; + DWORD bytes_returned = 0; + int result = SOCKET_ERROR; + { + talk_base::CritScope crit_scope(&crit_sect_); + if (!ws_handle_) + return false; + ASSERT(!event_handle_); + reset(event_handle_, ::CreateEvent(NULL, FALSE, FALSE, NULL)); + if (!event_handle_) { + LOG_F(LS_WARNING) << "failed to CreateEvent"; + return false; + } + WSAOVERLAPPED overlapped = {0}; + overlapped.hEvent = get(event_handle_); + WSACOMPLETION completion; + ::SetZero(completion); + completion.Type = NSP_NOTIFY_EVENT; + completion.Parameters.Event.lpOverlapped = &overlapped; + + LOG_F(LS_INFO) << "calling WSANSPIoctl"; + // Do a non-blocking request for change notification. event_handle_ + // will get signaled when there is a change, so we wait on it later. + // It can also be signaled by Close() in order allow clean termination. + result = ::WSANSPIoctl(ws_handle_, + SIO_NSP_NOTIFY_CHANGE, + &junk1, + 0, + &junk2, + 0, + &bytes_returned, + &completion); + } + if (NO_ERROR != result) { + result = ::WSAGetLastError(); + if (WSA_IO_PENDING != result) { + LOG_F(LS_WARNING) << "failed: " << result; + reset(event_handle_); + return false; + } + } + LOG_F(LS_INFO) << "waiting"; + WaitForSingleObject(get(event_handle_), INFINITE); + reset(event_handle_); + LOG_F(LS_INFO) << "changed"; + return true; + } + + private: + int Initialize() { + WSADATA wsa_data; + LOG_F(LS_INFO) << "calling WSAStartup"; + int result = ::WSAStartup(MAKEWORD(2, 2), &wsa_data); + if (result != ERROR_SUCCESS) { + LOG_F(LS_ERROR) << "failed:" << result; + return result; + } + + WSAQUERYSET query_set = {0}; + query_set.dwSize = sizeof(WSAQUERYSET); + query_set.dwNameSpace = NS_NLA; + // Initiate a client query to iterate through the + // currently connected networks. + if (0 != ::WSALookupServiceBegin(&query_set, LUP_RETURN_ALL, + &ws_handle_)) { + result = ::WSAGetLastError(); + LOG_F(LS_INFO) << "WSACleanup 2"; + ::WSACleanup(); + ASSERT(ws_handle_ == NULL); + ws_handle_ = NULL; + return result; + } + return 0; + } + talk_base::CriticalSection crit_sect_; + HANDLE ws_handle_; + scoped_event event_handle_; + DISALLOW_COPY_AND_ASSIGN(PlatformNetworkInfo); +}; + +class AsyncNetworkAliveWin32 : public AsyncNetworkAlive { + public: + AsyncNetworkAliveWin32() { + } + + virtual ~AsyncNetworkAliveWin32() { + if (network_info_) { + delete network_info_; + network_info_ = NULL; + } + } + + protected: + // SignalThread Interface + virtual void DoWork() { + if (!network_info_) { + network_info_ = new PlatformNetworkInfo(); + } else { + // Since network_info is set, it means that + // we are suppose to wait for network state changes. + if (!network_info_->WaitForChange()) { + // The wait was aborted so we must be shutting down. + alive_ = false; + error_ = true; + return; + } + } + alive_ = network_info_->IsAlive(&error_); + } + + virtual void OnWorkStop() { + if (network_info_) { + network_info_->Close(); + } + } + + private: + DISALLOW_COPY_AND_ASSIGN(AsyncNetworkAliveWin32); +}; + +AsyncNetworkAlive* AsyncNetworkAlive::Create() { + return new AsyncNetworkAliveWin32(); +} + +} // namespace notifier diff --git a/chrome/browser/sync/notifier/base/win32/time_win32.cc b/chrome/browser/sync/notifier/base/win32/time_win32.cc new file mode 100644 index 0000000..34a53fe --- /dev/null +++ b/chrome/browser/sync/notifier/base/win32/time_win32.cc @@ -0,0 +1,158 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Time functions + +#include <time.h> +#include <windows.h> + +#include "chrome/browser/sync/notifier/base/time.h" + +#include "chrome/browser/sync/notifier/base/utils.h" +#include "talk/base/common.h" +#include "talk/base/logging.h" + +namespace notifier { + +time64 FileTimeToTime64(const FILETIME& file_time) { + return static_cast<time64>(file_time.dwHighDateTime) << 32 | + file_time.dwLowDateTime; +} + +void Time64ToFileTime(const time64& time, FILETIME* ft) { + ASSERT(ft); + + ft->dwHighDateTime = static_cast<DWORD>(time >> 32); + ft->dwLowDateTime = static_cast<DWORD>(time & 0xffffffff); +} + +void TmTimeToSystemTime(const struct tm& tm, SYSTEMTIME* sys_time) { + ASSERT(sys_time); + + SetZero(*sys_time); + // tm's year is 1900 based, systemtime's year is absolute + sys_time->wYear = tm.tm_year + 1900; + // tm's month is 0 based, but systemtime's month is 1 based + sys_time->wMonth = tm.tm_mon + 1; + sys_time->wDay = tm.tm_mday; + sys_time->wDayOfWeek = tm.tm_wday; + sys_time->wHour = tm.tm_hour; + sys_time->wMinute = tm.tm_min; + sys_time->wSecond = tm.tm_sec; +} + +void SystemTimeToTmTime(const SYSTEMTIME& sys_time, struct tm* tm) { + ASSERT(tm); + + SetZero(*tm); + // tm's year is 1900 based, systemtime's year is absolute + tm->tm_year = sys_time.wYear - 1900; + // tm's month is 0 based, but systemtime's month is 1 based + tm->tm_mon = sys_time.wMonth - 1; + tm->tm_mday = sys_time.wDay; + tm->tm_wday = sys_time.wDayOfWeek; + tm->tm_hour = sys_time.wHour; + tm->tm_min = sys_time.wMinute; + tm->tm_sec = sys_time.wSecond; +} + +time64 GetCurrent100NSTime() { + // In order to get the 100ns time we shouldn't use SystemTime + // as it's granularity is 1 ms. Below is the correct implementation. + // On the other hand the system clock granularity is 15 ms, so we + // are not gaining much by having the timestamp in nano-sec + // If we decise to go with ms, divide "time64 time" by 10000 + + FILETIME file_time; + ::GetSystemTimeAsFileTime(&file_time); + + time64 time = FileTimeToTime64(file_time); + return time; +} + +time64 TmToTime64(const struct tm& tm) { + SYSTEMTIME sys_time; + TmTimeToSystemTime(tm, &sys_time); + + FILETIME file_time; + SetZero(file_time); + if (!::SystemTimeToFileTime(&sys_time, &file_time)) { + return 0; + } + + return FileTimeToTime64(file_time); +} + +bool Time64ToTm(time64 t, struct tm* tm) { + ASSERT(tm); + + FILETIME file_time; + SetZero(file_time); + Time64ToFileTime(t, &file_time); + + SYSTEMTIME sys_time; + SetZero(sys_time); + if (!::FileTimeToSystemTime(&file_time, &sys_time)) { + return false; + } + + SystemTimeToTmTime(sys_time, tm); + + return true; +} + +bool UtcTimeToLocalTime(struct tm* tm) { + ASSERT(tm); + + SYSTEMTIME utc_time; + TmTimeToSystemTime(*tm, &utc_time); + + TIME_ZONE_INFORMATION time_zone; + if (::GetTimeZoneInformation(&time_zone) == TIME_ZONE_ID_INVALID) { + return false; + } + + SYSTEMTIME local_time; + if (!::SystemTimeToTzSpecificLocalTime(&time_zone, &utc_time, &local_time)) { + return false; + } + + SystemTimeToTmTime(local_time, tm); + + return true; +} + +bool LocalTimeToUtcTime(struct tm* tm) { + ASSERT(tm); + + SYSTEMTIME local_time; + TmTimeToSystemTime(*tm, &local_time); + + // Get the bias, which when added to local, gives UTC + TIME_ZONE_INFORMATION time_zone; + if (::GetTimeZoneInformation(&time_zone) == TIME_ZONE_ID_INVALID) { + return false; + } + + // By negating the biases, we can get translation from UTC to local + time_zone.Bias *= -1; + time_zone.DaylightBias *= -1; + time_zone.StandardBias *= -1; // this is 0 but negating for completness + + // We'll tell SystemTimeToTzSpecificLocalTime that the local time is actually + // UTC. With the negated bias, the "local" time that the API returns will + // actually be UTC. Casting the const off because + // SystemTimeToTzSpecificLocalTime's definition requires it, although the + // value is not modified. + SYSTEMTIME utc_time; + if (!::SystemTimeToTzSpecificLocalTime(&time_zone, &local_time, &utc_time)) { + return false; + } + + SystemTimeToTmTime(utc_time, tm); + + return true; +} + +} // namespace notifier |