轉自:Chromium 基礎庫使用說明
原文:
Important Abstractions and Data Structures
基礎howto介紹:chrome是如何調用啟動的:https://www.chromium.org/developers/how-tos/getting-around-the-chrome-source-code
Threading and Tasks in Chrome - Bigben - 部落格園 (cnblogs.com)
Chromium和WebKit的智能指針實作原理分析_老羅的Android之旅-CSDN部落格
Chromium多線程模型設計和實作分析_老羅的Android之旅-CSDN部落格
Chromium多線程通信的Closure機制分析_老羅的Android之旅-CSDN部落格
C++ in Chromium 101 - Codelab 程式設計開始 bind 多線程 回調 - Bigben - 部落格園 (cnblogs.com)
Chromium 提供了一個類似 WTF 的基礎庫,甚至包含了更多的内容。這個基礎庫在 Blink 之外被廣泛使用(Blink 裡面仍然使用的是 WTF),了解它的使用對我們實際的代碼編寫是十分重要的。本文主要介紹 Chromium 基礎庫包括的主要内容,并詳細說明一些重要類型的使用方式。如果需要了解某個特定目錄或者檔案的内容概要,學輝的這篇文檔可以提供一個不錯的全面索引,另外 Chromium 為所有的基礎庫類型都提供了完整的單元測試,通過閱讀單元測試代碼了解這些類型的使用也是很好的方式。
Chromium 基礎庫概覽
Chromium 基礎庫包括的内容十分繁雜,我把其中的主要部分大緻分為以下幾類:
- 容器類型
Chromium 的代碼主要使用 STL 容器類型,比如 std::vector,std::list,另外 GCC 和 MSVC 提供的 STL 擴充容器類型 hash_map 和 hash_set 也在 Chromium 中使用,不過統一放在 base 名字空間裡面,通過 base::hash_map,base_hash_set 使用。
在 STL 外,Chromium 基礎庫還提供了一些額外的容器類型比如 base::LinkedList,base::MRUCache 等。
容器類型代碼位于 containers 子目錄下。
- 智能指針
Chromium 提供了一篇官方的文檔 Smart Pointer Guidelines 講解了在 Chromium 裡面常見的幾種智能指針,最常見的包括 base::scoped_ptr,base::ScopedVector,base::WeakPtr 和 base::scoped_refptr。
智能指針代碼位于 memory 子目錄下。
- 回調函數
Chromium 基礎庫提供了 base::Bind 機制,可以将全局函數和成員函數,跟它的調用上下文綁定在一起,構成一個回調函數對象。這個回調函數對象可以被傳遞,被儲存,被當做消息發送到線程的消息循環裡面,最後我們可以通過這個回調函數對象的 Run 方法調用跟它關聯的函數。
回調函數代碼位于基礎庫的根目錄下。
- 線程相關
Chromium 基礎庫提供了大量跟線程相關的設施,包括平台線程的封裝類型 base::Thread,線程本地存儲 base::ThreadLocalPointer,消息循環 base::MessageLoop,線程同步設施 base::Lock,base::WaitableEvent 等等,還有原子操作和記憶體屏障的支援。
線程相關的代碼位于 threading,message_loop,synchronization 子目錄下,原子操作和記憶體屏障位于根目錄的 atomicops.h。
- 字串處理
Chromium 使用 std::string 作為字串容器,官方文檔 Chromium String usage 提供了在 Chromium 裡面字串使用的一些說明。另外 strings 子目錄下提供了一些針對字串的輔助操作設施。
- 檔案操作
Chromium 基礎庫的 base::File 提供了檔案相關的操作,相關的代碼位于根目錄和 files 子目錄下;
- 計時器
Chromium 基礎庫的 base::Timer 提供了計時器相關的操作,相關的代碼位于 timer 子目錄下;
- 日志和調試
Chromium 基礎庫提供了通用的日志輸出和各種調試輔助等機制,相關的代碼位于根目錄, debug 和 profile 子目錄下;
- 系統監控
包括系統狀态監控,電池狀态監控和記憶體監控,分别位于 system_monitor,power_monitor,和 memory 子目錄下;
- Android 相關
基礎庫的 android 子目錄下是 Android 平台相關的代碼,除了包括其它基礎類型的 Android 适配代碼外,還有一些 Android 平台特有的類型,像一些用于 JNI 支援的輔助類型。
除了上面列舉的部分外,基礎庫還包括的一些設施有程序,記憶體配置設定器,國際化支援,随機數生成,Base64編碼,Sha1編碼等等,還有一些難以歸類的工具類型。
LinkedList
base::LinkedList 是 std::list 的一個替代品,優點是當你擁有一個節點對象時,要删除這個節點隻需要 O(1) 的複雜度,并且插入節點不需要新增配置設定記憶體。能夠做到這一點是因為 LinkedList 要求節點類型必須以 LinkNode 作為基類,而 LinkNode 本身已經包含了指向前/後節點的指針。下面的代碼示範了 LinkedList 的常見用法:
-
class MyNodeType : public LinkNode<MyNodeType> {
-
...
-
};
-
LinkedList<MyNodeType> list;
-
LinkNode<MyNodeType>* n1 = ...;
-
LinkNode<MyNodeType>* n2 = ...;
-
LinkNode<MyNodeType>* n3 = ...;
-
list.Append(n1);
-
list.Append(n3);
-
n2->InsertBefore(n3);
-
for (LinkNode<MyNodeType>* node = list.head();
-
node != list.end();
-
node = node->next()) {
-
MyNodeType* value = node->value();
-
...
-
}
MRUCache
MRU 是 most recently used 的縮寫,MRUCache 提供了一個類似 Map 的容器類型,主要的差別是可以設定容器的最大容納個數,如果超過則自動移除最久不被使用的那個對象。
MRUCache 實際上還存在幾種不同的變種:
- MRUCache 是最常用的,它假設自身不擁有對象,當移除對象時不執行删除操作;
- OwningMRUCache 假設自己擁有對象,并要求存儲對象是使用指針類型,在移除對象時會執行删除操作;
- HashingMRUCache 跟 MRUCache 的差別是,它内部使用 base::hash_map 而不是 std::map 存儲對象,是以也要求鍵值對象支援 hash 操作;
按照官方文檔的說明,什麼時候我們應該使用什麼類型的智能指針:
- 擁有對象的時候
使用 scoped_ptr 或者 ScopedVector,它們可以使用來管理所擁有的非引用計數的堆配置設定對象。
- 不擁有對象的時候
使用 raw pointer 或者 WeakPtr。如果其它代碼擁有對象,但是你需要知道這個對象是否已經被銷毀,就使用 WeakPtr,當所關聯的對象被銷毀的時候 WeakPtr 會自動被置空。你可以通過 WeakPtr.get 方法獲得關聯對象的指針,如果傳回值為空則說明對象已經被銷毀。
- 使用引用計數對象的時候
使用 scoped_refptr,不過 Chromium 不鼓勵使用引用計數對象,特别是在多線程場景下,引用計數對象會使對象的擁有權難以确定和對象銷毀的順序和時機難以确定。
scoped_ptr
base::scoped_ptr 是 Chromium 裡面最常用的智能指針,一些常見的用法:
-
// We put a pointer into a smart pointer at construction time.
-
scoped_ptr<base::Value> value(base::JSONReader::Read(data));
-
scoped_ptr<Foo> foo_ptr(new Foo(...));
-
// ...Or by using reset().
-
scoped_ptr<Bar> bar_ptr; // Like "Bar* bar_ptr = NULL;".
-
bar_ptr.reset(new Bar(...)); // Now |bar_ptr| is non-NULL and owns the object.
-
// We can test the smart pointer directly or use get() to see the raw pointer underneath.
-
if (!value)
-
return false;
-
Foo* raw_ptr = foo_ptr.get();
-
// We can call through the smart pointer as if it were a pointer.
-
DictionaryValue* dict = NULL;
-
if (!value->GetAsDictionary(&dict))
-
return false;
當 scoped_ptr 作為函數參數使用時,這意味着函數的代碼會獲得參數對象的所有權,函數的調用者如果不是使用一個臨時的 scoped_ptr 的話,它需要使用 Pass() 方法來放棄自己的 scoped_ptr 對對象的所有權,例程如下:
-
// Foo() takes ownership of |bar|.
-
void Foo(scoped_ptr<Bar> bar);
-
...
-
scoped_ptr<Bar> bar_ptr(new Bar());
-
Foo(bar_ptr.Pass()); // Pass() makes |bar_ptr| NULL.
-
Foo(scoped_ptr<Bar>(new Bar())); // No need to use Pass() on temporaries.
如果函數傳回一個 scoped_ptr,這意味着函數的調用者獲得傳回對象的所有權,例程如下:
-
// Foo takes ownership of |bar|, and the caller takes ownership of the returned
-
// object.
-
scoped_ptr<Bar> Foo(scoped_ptr<Bar> bar) {
-
if (cond) {
-
return bar.Pass(); // Transfers ownership of |bar| back to
-
// the caller.
-
}
-
return scoped_ptr<Bar>(new Bar())); // No Pass() necessary on temporaries.
-
// Note that on this codepath, |bar| gets deleted here.
-
}
最後需要注意的是不應該在函數的參數和傳回值中使用 scoped_ptr 的指針或者引用形式(
scoped_ptr<>* scoped_ptr<>&
),它會模糊所有權的轉移,使最終誰擁有對象的所有權難以了解。
ScopedVector
在 STL 容器裡面存儲 scoped_ptr, 類似
std::vector<scoped_ptr<T> >
這樣的用法可能會有問題,比如下面的代碼:
-
std::vector<scoped_ptr<T> > vec;
-
...
-
// 對象的所有權會從 vec 轉移到 scoped_ptr p,并随着 p 被銷毀而銷毀!!!
-
scoped_ptr<T> p = vec[0];
因為上述代碼的危險性,是以 Chromium 不支援通過 STL 容器存儲 scoped_ptr,它提供了 base::ScopedVector 來滿足大部分這種需求,ScopedVector 擁有存儲在它内部的對象,并在移除對象的時候負責銷毀對象,如果 ScopedVector 本身被銷毀,它會銷毀它所存儲的所有對象。因為 ScopedVector 内部存儲的是 raw pointer,就不存在像
std::vector<scoped_ptr<T> >
這樣容易誤用的危險性。
-
base::ScopedVector<T> vec;
-
...
-
// 通過 raw pointer p 使用對象,不會有所有權的轉移
-
T* p = vec[0];
如果需要在其它 STL 容器裡面使用智能指針,希望在容器被銷毀或者移除元素時自動銷毀容器存儲的對象,可以考慮使用 linked_ptr。
WeakPtr
base::WeakPtr 是所謂的弱指針,Chromium 鼓勵更多使用 WeakPtr 而不是濫用需要引用計數的 scoped_refptr,因為 WeakPtr 明确不會擁有對象的所有權,也不會影響對象的銷毀順序。
base::WeakPtr 需要通過 base::WeakPtrFactory 建立,一般情況下它們使用的方式是這樣的:
-
class Controller {
-
public:
-
void SpawnWorker() { Worker::StartNew(weak_factory_.GetWeakPtr()); }
-
void WorkComplete(const Result& result) { ... }
-
private:
-
// Member variables should appear before the WeakPtrFactory, to ensure
-
// that any WeakPtrs to Controller are invalidated before its members
-
// variable's destructors are executed, rendering them invalid.
-
WeakPtrFactory<Controller> weak_factory_;
-
};
-
class Worker {
-
public:
-
static void StartNew(const WeakPtr<Controller>& controller) {
-
Worker* worker = new Worker(controller);
-
// Kick off asynchronous processing...
-
}
-
private:
-
Worker(const WeakPtr<Controller>& controller)
-
: controller_(controller) {}
-
void DidCompleteAsynchronousProcessing(const Result& result) {
-
if (controller_)
-
controller_->WorkComplete(result);
-
}
-
WeakPtr<Controller> controller_;
-
};
- 需要支援 WeakPtr 的類型 Controller 擁有一個 WeakPtrFactory 的成員變量,外部擷取的 WeakPtr 都是通過這個 WeakPtrFactory 建立的;
- 當 Controller 對象被銷毀時,它的 WeakPtrFactory 成員變量也會同時被銷毀,WeakPtrFactory 被銷毀的同時會将所有通過它建立的 WeakPtr 置空;
- Controller 的 WeakPtrFactory 的成員變量一般放在最後面,這樣它就是第一個被銷毀的成員變量,似乎沒有太大意義,不過 Chromium 習慣使用這樣的方式;
在多線程環境下使用 WeakPtr 和 WeakPtrFactory 需要注意,它們隻支援這樣的方式:
- WeakPtrFactory 和 WeakPtr 屬于建立它們的線程,也隻能在建立它們的線程将 WeakPtr 置空,檢查一個 WeakPtr 是否為空,和通路 WeakPtr 指向的對象;
- 屬于線程 A 的 WeakPtr 可以傳遞給 線程 B,線程 B 不能直接使用這個 WeakPtr,這不是線程安全的,但是它可以使用這個 WeakPtr 往線程 A 發送任務(PostTask),因為任務是線上程 A 執行的,是以任務執行代碼本身可以使用這個 WeakPtr;
scoped_refptr
用于支援引用計數對象的智能指針,要求對象類型繼承至 RefCounted 或者 RefCountedThreadSafe,後者是線程安全的。Chromium 因為曆史遺留的緣故,目前的代碼中使用 scoped_refptr 的地方還比較多,但是目前官方已經不鼓勵 scoped_refptr 的使用,認為它會導緻對象的所有權,和銷毀的順序和時機難以确定,并認為絕大部分情況下 scoped_refptr 都可以使用 scoped_ptr 和 WeakPtr 來取代,設計本身也不應該過多依賴多個線程共享對象這種方式。
下面是一些簡單的使用例程:
-
class MyFoo : public RefCounted<MyFoo> {
-
...
-
};
-
void some_function() {
-
scoped_refptr<MyFoo> foo = new MyFoo();
-
foo->Method(param);
-
// |foo| is released when this function returns
-
}
-
void some_other_function() {
-
scoped_refptr<MyFoo> foo = new MyFoo();
-
...
-
foo = NULL; // explicitly releases |foo|
-
...
-
if (foo)
-
foo->Method(param);
-
}
-
{
-
scoped_refptr<MyFoo> a = new MyFoo();
-
scoped_refptr<MyFoo> b;
-
b.swap(a);
-
// now, |b| references the MyFoo object, and |a| references NULL.
-
}
-
{
-
scoped_refptr<MyFoo> a = new MyFoo();
-
scoped_refptr<MyFoo> b;
-
b = a;
-
// now, |a| and |b| each own a reference to the same MyFoo object.
-
}
linked_ptr
linked_ptr 行為上有些類似 scoped_refptr,但是不需要對象本身支援引用計數,它是通過将所有指向同一個對象的 linked_ptr 連結成一條鍊來實作引用計數的,當一個 linked_ptr 從另外一個 linked_ptr 拷貝時,它會把自身加入這條鍊,而這個 linked_ptr 被銷毀時,它會把自身從這條鍊移除,如果它是最後一個,則同時銷毀指向的對象。
linked_ptr 實際上有可能比 scoped_refptr 更危險,它使得對象的持有者和銷毀時機變得更不明确,同時也不是線程安全的。是以 linked_ptr 一般隻是用在 STL 容器上面,容器持有這些對象,并且在容器本身被銷毀時銷毀對象,這樣就不會産生太多混亂。
base::SupportsUserData 的實作裡面使用了 linked_ptr,用來在一個 std::map 裡面存儲 User Data。
-
typedef std::map<const void*, linked_ptr<Data> > DataMap;
-
// Externally-defined data accessible by key.
-
DataMap user_data_;
-
SupportsUserData::Data* SupportsUserData::GetUserData(const void* key) const {
-
DataMap::const_iterator found = user_data_.find(key);
-
if (found != user_data_.end())
-
return found->second.get();
-
return NULL;
-
}
-
void SupportsUserData::SetUserData(const void* key, Data* data) {
-
user_data_[key] = linked_ptr<Data>(data);
-
}
Chromium 提供了 base::Bind 和模版類型 base::Callback 對函數回調提供了支援,下面是一個簡單的使用例程,将一個全局函數綁定到一個 Callback 對象,并通過 Callback.Run 調用這個函數:
-
int Return5() { return 5; }
-
base::Callback<int(void)> func_cb = base::Bind(&Return5);
-
LOG(INFO) << func_cb.Run(); // Prints 5.
如果要綁定一個類的成員函數,我們需要為 Bind 方法提供這個類的一個執行個體對象,把它跟 Callback 對象綁定,為了保證這個對象在 Callback 對象被執行時仍然存活,或者 Callback 對象能夠知道這個對象已經被銷毀,我們需要提供一個 scoped_refptr 或者 WeakPtr,通過 base::Unretained(ptr) 用 raw pointer 也可以,不過後果自負... 早期 Chromium 的代碼使用 scoped_refptr 比較多,現在 Chromium 更傾向于使用 WeakPtr,當然使用 WeakPtr 時我們要注意這個 Callback 隻能在 WeakPtr 所屬的線程中被調用,因為它是非線程安全的,下面是一個使用 scoped_refptr 的例子:
-
class Ref : public base::RefCountedThreadSafe<Ref> {
-
public:
-
int Foo() { return 3; }
-
void PrintBye() { LOG(INFO) << "bye."; }
-
};
-
scoped_refptr<Ref> ref = new Ref();
-
base::Callback<void(void)> ref_cb = base::Bind(&Ref::Foo, ref);
-
LOG(INFO) << ref_cb.Run(); // Prints out 3.
如果綁定的函數需要參數,我們可以事先綁定所有參數對象到 Callback 裡面,也可以事先不綁定參數,甚至可以事先隻綁定一部分參數,事先綁定所有參數的 Callback 在 Chromium 裡面稱為閉包 Closure:
-
void MyFunc(int i, const std::string& str) {}
-
base::Callback<void(int, const std::string&)> cb = base::Bind(&MyFunc);
-
cb.Run(23, "hello, world");
-
void MyFunc(int i, const std::string& str) {}
-
base::Callback<void(void)> cb = base::Bind(&MyFunc, 23, "hello world");
-
cb.Run();
-
base::Closure cb = base::Bind(&MyClass::MyFunc, this, 23, "hello world");
如果想讓 Callback 對象擁有跟它綁定的類對象或者參數對象,也可以使用 base::Owned 或者 base::Passed 方法,分别針對 raw pointer 和 scoped_ptr,如果是 scoped_ptr 類型參數的話,在調用時 Callback 就會将這個參數對象的所有權轉移給被回調的函數,最後 Callback 對象被銷毀時會自動銷毀綁定的類對象和參數對象(如果還擁有這個參數對象的話):
-
MyClass* myclass = new MyClass;
-
base::Bind(&MyClass::Foo, base::Owned(myclass));
-
void TakesOwnership(scoped_ptr<Foo> arg) {}
-
scoped_ptr<Foo> f(new Foo);
-
// f becomes null during the following call.
-
base::Closure cb = base::Bind(&TakesOwnership, base::Passed(&f));
總而言之,在使用 Chromium 的回調函數機制時,一定要非常清楚跟 Callback 對象綁定的類對象和參數對象的所有權和生命周期,避免在 Callback 被調用時,通路到已經被銷毀的對象。
線程和消息循環
base::Thread 是 Chromium 提供的對平台線程的封裝,并自帶了消息循環 base::MessageLoop,如果需要一個不用消息循環的線程,可以考慮使用 base::SimpleThread。
一個繼承 base::Thread 的自己的線程類,可能需要複寫 Init 和 Cleanup 方法,它們在這個線程中被調用,分别位于消息循環啟動和停止的時候。
-
class InProcessRendererThread : public base::Thread {
-
public:
-
...
-
protected:
-
virtual void Init() override;
-
virtual void CleanUp() override;
-
...
-
};
我們可以通過 Thread.message_loop 或者 Thread.message_loop_proxy 方法擷取這個線程的消息循環,後者傳回的是 MessageLoopProxy,在 Chromium 裡面使用 MessageLoopProxy 比直接使用 MessageLoop 要更普遍,并且通過 scoped_refptr 的方式使用 MessageLoopProxy 比通過 raw pointer 的方式使用 MessageLoop 也更安全,通過下面的兩種方式可以獲得目前運作線程的 MessageLoopProxy。
-
MessageLoop::current()->message_loop_proxy()
-
MessageLoopProxy::current()
MessageLoopProxy 繼承了接口 SequencedTaskRunner,後者又繼承了接口 TaskRunner,是以 MessageLoopProxy 實作了一系列的 PostXXXTask 的方法。一個 Task 實際上就是一個 Closure,如前所述 Closure 就是一個預先綁定了所有參數對象的 Callback 對象。通過 MessageLoopProxy PostTask 就相當于發送一個消息給這個 MessageLoopProxy 所屬的線程,這個被發送的 Callback 對象将會在 MessageLoopProxy 所屬的線程執行,跟 Callback 對象綁定的函數将會被調用。
PostXXXTask 有若幹變種,包括延遲的時間,是否是 Non-Nestable。延遲時間比較容易了解,不需要延遲則為 0,而 Non-Nestable 的意思是 - 如果 Task T1 在執行過程中 Post Task T2 到目前線程的 MessageLoop,并且 T1 接着直接調用 MessageLoop 的 Run,或者 RunLoop 的 Run 方法,相當于要求 MessageLoop 在目前消息循環中進入一個子循環,馬上執行其它等待中的任務,在這種狀況下 MessageLoop 進入了 Nested 狀态,如果 T2 是 Non-Nestable,Chromium 将會保證 T2 在這種情況下絕對不會被執行,如果 T2 不是 Non-Nestable,就有可能在會被執行。
下面是一個簡單使用例程:
-
scoped_refptr<base::MessageLoopProxy> ui_loop_;
-
base::WeakPtr<SharedRendererState> ui_thread_weak_ptr_;
-
void SharedRendererState::PostExternalDrawConstraintsToChildCompositor(
-
const ParentCompositorDrawConstraints& parent_draw_constraints) {
-
if (UpdateDrawConstraints(parent_draw_constraints)) {
-
// No need to hold the lock_ during the post task.
-
ui_loop_->PostTask(
-
FROM_HERE,
-
base::Bind(&SharedRendererState::UpdateParentDrawConstraintsOnUIThread,
-
ui_thread_weak_ptr_));
-
}
-
}
如果需要任務執行後原線程獲得通知,可以使用 PostTaskAndReply 方法,參考下面的例程,task 執行後,reply 會在調用 PostTaskAndReplay 的原線程被調用,并且 task 和 reply 對象都保證在原線程被銷毀,這樣我們可以在 task 和 reply 上綁定必須要在原線程銷毀的對象。另外一些需要注意的地方:
- task 綁定的類對象會作為參數傳遞給 reply 的回調函數;
- reply 綁定的類對象 DataLoder 不是線程安全的,它通過 WeakPtr 跟 reply 綁定,可以提前被銷毀,reply 會被自動取消;
-
bool PostTaskAndReply(const tracked_objects::Location& from_here,
-
const Closure& task,
-
const Closure& reply);
-
class DataBuffer : public RefCountedThreadSafe<DataBuffer> {
-
public:
-
// Called to add data into a buffer.
-
void AddData(void* buf, size_t length);
-
...
-
};
-
class DataLoader : public SupportsWeakPtr<DataLoader> {
-
public:
-
void GetData() {
-
scoped_refptr<DataBuffer> buffer = new DataBuffer();
-
target_thread_.message_loop_proxy()->PostTaskAndReply(
-
FROM_HERE,
-
base::Bind(&DataBuffer::AddData, buffer),
-
base::Bind(&DataLoader::OnDataReceived, AsWeakPtr(), buffer));
-
}
-
private:
-
void OnDataReceived(scoped_refptr<DataBuffer> buffer) {
-
// Do something with buffer.
-
}
-
};
如果 PostTask 之後,我們又希望取消它,可以使用 base::CancelableTaskTracker 來 PostTask,CancelableTaskTracker 本身不是線程安全的,它的建立,銷毀,PostTask,Cancel 都必須在同一個線程。下面是一個簡單的使用例子:
-
Thread worker_thread("worker thread");
-
worker_thread.Start();
-
CancelableTaskTracker::TaskId task_id =
-
task_tracker_.PostTaskAndReply(worker_thread.message_loop_proxy().get(),
-
FROM_HERE,
-
Bind(&DoNothing),
-
Bind(&DoNothing));
-
task_tracker_.TryCancel(task_id);
線程本地存儲
Chromium 提供了 ThreadLocalPointer,它是平台相關的線程本地存儲機制的封裝,可以存放一個 raw pointer,如果需要的是 bool 類型的變量,ThreadLocalBoolean 提供了更簡單的使用方式。ThreadLocalPointer 的一個簡單例程:
-
// My class is logically attached to a single thread. We cache a pointer
-
// on the thread it was created on, so we can implement current().
-
MyClass::MyClass() {
-
DCHECK(Singleton<ThreadLocalPointer<MyClass> >::get()->Get() == NULL);
-
Singleton<ThreadLocalPointer<MyClass> >::get()->Set(this);
-
}
-
MyClass::~MyClass() {
-
DCHECK(Singleton<ThreadLocalPointer<MyClass> >::get()->Get() != NULL);
-
Singleton<ThreadLocalPointer<MyClass> >::get()->Set(NULL);
-
}
-
// Return the current MyClass associated with the calling thread, can be
-
// NULL if there isn't a MyClass associated.
-
MyClass* MyClass::current() {
-
return Singleton<ThreadLocalPointer<MyClass> >::get()->Get();
-
}
線程同步
base::Lock 是平台相關鎖的封裝,base::AutoLock 提供了一個自動加鎖/解鎖的輔助類,這部分都比較容易了解。base::ConditionVariable 是平台相關的條件量的封裝,跟其它庫的 Condition 類型有些不同的是,它需要在構造時就指定對應的鎖,而不是在 Wait 的時候才指定。
為了友善實作線程同步消息,Chromium 還提供了 base::WaitableEvent (如果是位于 cc 子產品的代碼,也可以使用 cc::CompletionEvent,它是 base::WaitableEvent 的封裝),WaitableEvent 構造函數的第一個參數 manual_reset 的含義是,如果它為 false,一個已經 signaled 的 WaitableEvent 在被查詢 IsSignaled 後會自動恢複到 unsignaled 的狀态,是以一般沒有特殊需要第一個參數都應該為 true。下面是一個簡單的線程同步消息處理的例子:
-
template <typename T>
-
static void RunTaskWithResult(base::Callback<T(void)> task,
-
T* result,
-
base::WaitableEvent* completion) {
-
*result = task.Run();
-
completion->Signal();
-
}
-
base::WaitableEvent completion(true, false);
-
bool result = false;
-
QueueTask(
-
base::Bind(&RunTaskWithResult<bool>, init_task, &result, &completion));
-
completion.Wait();
base::WaitableEventWatcher 提供了 WaitableEvent 異步響應的使用方式,請看下面的例程,我們可以通過 WaitableEventWatcher 監控某個 WaitableEvent,并在它被 Signal 的時候觸發事先設定的回調函數,實際内部實作是當 WaitableEvent 被 Signal 時,WaitableEventWatcher 事先設定的 Callback 對象會被發送到 StartWatching 的調用線程的消息循環裡面:
-
class MyClass {
-
public:
-
void DoStuffWhenSignaled(WaitableEvent *waitable_event) {
-
watcher_.StartWatching(waitable_event,
-
base::Bind(&MyClass::OnWaitableEventSignaled, this);
-
}
-
private:
-
void OnWaitableEventSignaled(WaitableEvent* waitable_event) {
-
// OK, time to do stuff!
-
}
-
base::WaitableEventWatcher watcher_;
-
};
Chromium 主要使用 std::string 作為字串類型,std::string 的一個主要問題是它本身不包含編碼資訊,是以 Chromium 約定 std::string 使用 UTF-8 編碼,基礎庫裡面還提供了 base::string16,string16 使用 UTF-16 編碼。
Chromium 另外還有一個 base::StringPiece 類型,它類似 WTF 裡面的 CString,基本上就是 C 風格字串的一個簡單封裝,StringPiece 通常隻是用來傳遞一塊 string data 或者 raw data,它本身并不擁有這些資料,銷毀時也不會釋放資料。
下面是一些使用時的注意事項:
- 使用 string.empty() 做空串檢查;
- 字串常量使用 char[] 而不是 std::string,比如
const char kFoo[] = “foo”;
- 在函數輸入參數中使用 std::string,最好使用引用常量的方式避免拷貝;
- 在循環的 inner loop 裡面,一般應該避免臨時 std::string 對象建立;
Chromium 提供的一些字串處理的輔助方法,比如字串格式化,分割,數值字串類型轉換,比較,替換等等,位于 strings 子目錄下,都比較簡單,這裡就不再詳細說明了。
PathService
base::PathService 提供了一種設定和擷取一些預定義用途目錄的機制,在 Android 上,我們需要的目錄定義在 base_path_android.h 和 ui_base_path.h 裡面,另外 PathService.java 提供了在 Java 端設定路徑的功能。
-
enum {
-
PATH_ANDROID_START = 300,
-
DIR_ANDROID_APP_DATA, // Directory where to put Android app's data.
-
DIR_ANDROID_EXTERNAL_STORAGE, // Android external storage directory.
-
PATH_ANDROID_END
-
};
-
PathService.override(PathService.DIR_MODULE, "/system/lib/");
-
final int DIR_RESOURCE_PAKS_ANDROID = 3003;
-
PathService.override(DIR_RESOURCE_PAKS_ANDROID,
-
"/system/framework/webview/paks");
File
base::File 提供了平台相關的檔案對象的封裝,可以通過它對檔案和目錄進行操作,包括建立,讀寫檔案等等。base::FilePath 提供了一個檔案或者目錄路徑的封裝。
base::FileProxy 提供了一種異步檔案操作的方法,你可以為 FileProxy 設定一個 TaskRunner,比如某個線程的 MessageLoopProxy,然後在 FileProxy 上執行的操作實際上都是由這個 TaskRunner 所屬的線程異步執行,FileProxy 提供的方法跟 File 基本一緻,一般後面會增加一個用于響應操作結果的 Callback 對象,這個 Callback 對象會在原調用線程執行。FileProxy 有一個限制是不能同時 Proxy 多個操作,隻有完成一個操作後才能執行下一個操作。
下面是一個簡單的例程,我們在另外一個 file_thread_ 線程建立或者打開一個檔案,當檔案建立或者打開後,原線程會執行 DidCreateOrOpen 函數處理操作結果:
-
TaskRunner* file_task_runner() const {
-
return file_thread_.message_loop_proxy().get();
-
}
-
void DidCreateOrOpen(File::Error error) {
-
error_ = error;
-
MessageLoop::current()->QuitWhenIdle();
-
}
-
FileProxy proxy(file_task_runner());
-
proxy.CreateOrOpen(
-
test_path(),
-
File::FLAG_CREATE | File::FLAG_READ,
-
Bind(&FileProxyTest::DidCreateOrOpen, weak_factory_.GetWeakPtr()));
-
MessageLoop::current()->Run();
-
EXPECT_EQ(File::FILE_OK, error_);
-
EXPECT_TRUE(proxy.IsValid());
-
EXPECT_TRUE(proxy.created());
-
EXPECT_TRUE(PathExists(test_path()));
Chromium 還提供很多檔案相關的輔助類:
- base::FileEnumerator 提供了枚舉某個 FilePath 下面的子檔案的功能;
- base::FilePathWatcher 提供了監控某個檔案或者目錄變化的功能;
- base::ImportantFileWriter 提供了另外一種檔案寫入方式,避免應用崩潰導緻檔案寫入一半,資料不完整的狀況,原理是先寫入一個臨時檔案,寫完後再重命名;
- base::MemoryMappedFile 提供了一種将隻讀檔案全部或者部分映射到記憶體,讀取檔案相當于記憶體通路,加快讀取的速度的機制;
- file_util.h 裡面提供大量檔案操作的輔助方法,比如 CreateTemporaryFile,GetFileSize 等等;
base::Timer 實際上相當于 MessageLoop::PostDelayedTask 的封裝,對外提供了一次性或者不斷重複的計時器功能。Timer 的構造函數裡面 retain_user_task 的含義是,當 Timer 被 Stop 的時候,關聯的任務是否被保留,預設值為 true,也就是保留而不置空。跟 WTF 裡面的 Timer 一樣,base::Timer 是有線程歸屬性的,它屬于調用 Start 或者 Reset 方法的線程,設定的任務也在這個線程裡面執行。
-
base::Timer timer(false, false);
-
EXPECT_FALSE(timer.IsRunning());
-
timer.Start(FROM_HERE, TimeDelta::FromDays(1),
-
base::Bind(&TimerTestCallback));
-
EXPECT_TRUE(timer.IsRunning());
-
timer.Stop();
-
EXPECT_FALSE(timer.IsRunning());
-
EXPECT_TRUE(timer.user_task().is_null());
base::ElapsedTimer 提供一個簡單的方法給程式計算某些操作的耗時。
日志輸出
Chromeium 提供了 LOG,DLOG,VLOG 幾種輸出日志的方式,類似下面這樣的代碼:
-
LOG(INFO) << "Found " << num_cookies << " cookies";
-
LOG_IF(INFO, num_cookies > 10) << "Got lots of cookies";
INFO 是輸出日志的級别,一共包括 INFO,WARNING,ERROR 和 FATAL 這四種,其中 FATAL 會在日志輸出後自動引發一個崩潰。LOG_IF 提供了額外的條件判斷,條件成立時才輸出日志。DLOG 跟 LOG 的差別是 DLOG 隻在 DEBUG 版本才生效,而 VLOG 跟 LOG 的差別是可以用 verbose 級别來控制是否生效,比如:
-
VLOG(1) << "I'm printed when you run the program with --v=1 or more";
-
VLOG(2) << "I'm printed when you run the program with --v=2 or more";
當啟動開關 --v=1 時 VLOG 1 以上的級别生效。
調用跟蹤
Chromium 提供了強大的 Tracing 機制,在 Android 上也對接了 Android Systrace 機制,是以對我們來說,最簡單的方式就打開 Chromium Tracing,然後通過 Android Systrace 捕捉跟蹤的輸出。
在 TestShell 裡面,我們可以通過設定 BrowserActivity.ENABLE_ATRACE 開啟 Chromium Tracing,或者通過菜單開啟,然後調用 Android systrace 指令捕捉即可。
如果要增加跟蹤的方法,最簡單的方式是使用如下代碼:
-
TRACE_EVENT0("android_webview", "BrowserViewRenderer::OnDrawHardware");
更複雜的跟蹤方式可以參考 trace_event.h 裡面的說明文檔。
調用堆棧
base::debug::StackTrace 提供了調用堆棧列印的功能,StackTrace 會在被構造的時候存儲目前的調用堆棧資料,然後可以通過它直接列印到控制台或者擷取相應的文本,在 Android 上是通過 logcat 輸出 error 日志。一般來說 StackTrace 可以作為函數的臨時變量輸出目前函數的調用堆棧,也可以作為對象的成員變量記錄對象建立時的調用堆棧。StackTrace 輸出的是位址資訊,還需要使用符号表和對應的工具翻譯成可讀的函數名字。
下面是一個簡單的使用例程和輸出的結果:
-
void AwContents::Destroy(JNIEnv* env, jobject obj) {
-
base::debug::StackTrace().Print();
-
...
-
}
-
#00 0x751c38a1 /data/app-lib/com.uc.webkit.test-1/libwebviewuc.so+0x001f08a1
-
#01 0x4153f30f /system/lib/libdvm.so+0x0001d30f
記憶體監控
Chromium 提供了 MemoryPressureListener 接口,在 Android 上實際對接了 ComponentCallbacks2 的 onTrimMemory 和 onLowMemory(MemoryPressureListener.java)。
使用方式如下:
-
void OnMemoryPressure(MemoryPressureLevel memory_pressure_level) {
-
...
-
}
-
// Start listening.
-
MemoryPressureListener* my_listener =
-
new MemoryPressureListener(base::Bind(&OnMemoryPressure));
-
...
-
// Stop listening.
-
delete my_listener;
- 建立 MemoryPressureListener 對象,并傳入一個 Callback 對象,啟動監聽;
- 銷毀 MemoryPressureListener 停止監聽;
- Callback 對象會在建立 MemoryPressureListener 的線程被調用,調用是異步的,通過線程消息,即使這個線程就是 Android 的 UI 線程;
- MemoryPressureLevel 包括 MEMORY_PRESSURE_MODERATE 和 MEMORY_PRESSURE_CRITICAL,跟 Android ComponentCallbacks2.onTrimMemory 和 onLowMemory 的對應關系如下面代碼所示;
-
// Modules are advised to free buffers that are cheap to re-allocate and not
-
// immediately needed.
-
DEFINE_MEMORY_PRESSURE_LEVEL(MEMORY_PRESSURE_MODERATE, 0)
-
// At this level, modules are advised to free all possible memory.
-
// The alternative is to be killed by the system, which means all memory will
-
// have to be re-created, plus the cost of a cold start.
-
DEFINE_MEMORY_PRESSURE_LEVEL(MEMORY_PRESSURE_CRITICAL, 2)
-
public void onTrimMemory(int level) {
-
maybeNotifyMemoryPresure(level);
-
}
-
public void onLowMemory() {
-
nativeOnMemoryPressure(MemoryPressureLevelList.MEMORY_PRESSURE_CRITICAL);
-
}
-
public static void maybeNotifyMemoryPresure(int level) {
-
if (level >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE) {
-
nativeOnMemoryPressure(MemoryPressureLevelList.MEMORY_PRESSURE_CRITICAL);
-
} else if (level >= ComponentCallbacks2.TRIM_MEMORY_BACKGROUND ||
-
level == ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL) {
-
// Don't notifiy on TRIM_MEMORY_UI_HIDDEN, since this class only
-
// dispatches actionable memory pressure signals to native.
-
nativeOnMemoryPressure(MemoryPressureLevelList.MEMORY_PRESSURE_MODERATE);
-
}
-
}
base::BuildInfo 基本上等同于 Android 的 android.os.Build 的 Native 版本,可以通過它獲得一些 Build 相關的資訊,比如系統版本号等。
path_utils.h 提供一些輔助函數,用于擷取 Android 系統或者應用相關的特定目錄,比如 GetDataDirectory 傳回目前應用的 Data 目錄。
jni_string.h 提供了一些跟字串相關的輔助函數,用于 Java String 和 Native String 之間的轉換,比如 ConvertJavaStringToUTF8,ConvertUTF8ToJavaString 等。
jni_array.h 提供了一些跟數組相關的輔助函數,比如 ToJavaXXXArray 将一個 Native 數組轉換成一個 Java 數組對象,轉換過程中原始的資料會被拷貝。
ScopedJavaLocalRef, ScopedJavaGlobalRef,JavaObjectWeakGlobalRef
base::android::ScopedJavaLocalRef 和 base::android::ScopedJavaGlobalRef 提供了在 Native 端持有一個 Java 對象,并在 Scoped 對象被銷毀時自動解除該 Java 對象引用的機制,有點類似是針對 Java 對象的 scoped_refptr。
ScopedJavaLocalRef 對應 JNI 的 LocalRef,作為棧對象在函數内部使用,一般用于在函數結束時自動解除關聯的 Java 對象的引用,或者作為函數的傳回值傳遞 Java 對象的引用給它的調用者:
-
bool GetDatabaseDirectory(FilePath* result) {
-
JNIEnv* env = AttachCurrentThread();
-
ScopedJavaLocalRef<jstring> path =
-
Java_PathUtils_getDatabaseDirectory(env, GetApplicationContext());
-
FilePath data_path(ConvertJavaStringToUTF8(path));
-
*result = data_path;
-
return true;
-
}
ScopedJavaGlobalRef 對應 JNI 的 GlobalRef,一般作為類的成員變量,或者在需要超過某個函數的調用生命周期去持有一個 Java 對象的狀況下使用。
下面的例子示範了一個異步回調的處理,我們需要一個 ScopedJavaGlobalRef 保證這個關聯的 Java 對象在回調函數被真正執行時任然存活而不會被銷毀,base::Owened 将 j_callback 的擁有權轉移給 base::Bind 建立的 Callback 對象。
-
void GenerateMHTMLCallback(ScopedJavaGlobalRef<jobject>* callback,
-
const base::FilePath& path, int64 size) {
-
JNIEnv* env = AttachCurrentThread();
-
// Android files are UTF8, so the path conversion below is safe.
-
Java_AwContents_generateMHTMLCallback(
-
env,
-
ConvertUTF8ToJavaString(env, path.AsUTF8Unsafe()).obj(),
-
size, callback->obj());
-
}
-
} // namespace
-
void AwContents::GenerateMHTML(JNIEnv* env, jobject obj,
-
jstring jpath, jobject callback) {
-
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
ScopedJavaGlobalRef<jobject>* j_callback = new ScopedJavaGlobalRef<jobject>();
-
j_callback->Reset(env, callback);
-
base::FilePath target_path(ConvertJavaStringToUTF8(env, jpath));
-
web_contents_->GenerateMHTML(
-
target_path,
-
base::Bind(&GenerateMHTMLCallback, base::Owned(j_callback), target_path));
-
}
base::JavaObjectWeakGlobalRef 用于持有一個 Java 對象的弱引用,對應 JNI 的 WeakGlobalRef,當需要使用這個 Java 對象時可以通過 JavaObjectWeakGlobalRef.get 傳回一個 ScopedJavaLocalRef。
工具類型
這一節的内容包括一些比較零散,無法歸類的工具類型。
SupportsUserData
base::SupportsUserData 是用來給一個對象增加 UserData 支援的輔助類,User Data,也叫 Client Data,一般使用 Key-Value 的方式存儲,是這個對象的使用者将自己或者其它的使用者需要用到的一些資料附加在這個對象上面的一種機制,對象本身隻是作為這些資料的一個載體。
需要承載 User Data 的類,需要繼承 SupportsUserData,比如 content::WebContents,而需要作為 User Data 存儲的類型,需要繼承 SupportsUserData::Data,使用 void* 指針做 key。SupportsUserData 是非線程安全的,如果跨線程使用,需要使用者自己保證線程安全。
LazyInstance
LazyInstance 提供了一種延遲建立全局靜态對象的方式,它的優點是:
- 它預先在程式的靜态記憶體區配置設定了對象的記憶體,當對象建立時就不需要在堆上配置設定記憶體,加快了對象建立的速度和減少堆記憶體碎片;
- 它的對象建立是線程安全的,不用擔心多線程競争的狀況;
- 它延遲對象的建立到第一次使用的時候,避免在程式啟動時建立,減少了啟動的時間開銷;
總的來說 LazyInstance 就像是函數内部的靜态對象的線程安全版本,下面是使用的例程:
-
static LazyInstance<MyClass> my_instance = LAZY_INSTANCE_INITIALIZER;
-
void SomeMethod() {
-
my_instance.Get().SomeMethod(); // MyClass::SomeMethod()
-
MyClass* ptr = my_instance.Pointer();
-
ptr->DoDoDo(); // MyClass::DoDoDo
-
}
如果明确不需要銷毀對象,不需要調用析構函數,可以使用 Leaky 類型定義(實際上在 CAW 上,用不用 Leaky 都一樣,參看下面的 Singleton):
-
base::LazyInstance<GlobalTileManager>::Leaky g_tile_manager =
-
LAZY_INSTANCE_INITIALIZER;
Singleton
一般友善自己的類型實作單例模式的輔助類,使用的例程如下:
-
// In your header:
-
template <typename T> struct DefaultSingletonTraits;
-
class FooClass {
-
public:
-
static FooClass* GetInstance();
-
void Bar() { ... }
-
private:
-
FooClass() { ... }
-
friend struct DefaultSingletonTraits<FooClass>;
-
DISALLOW_COPY_AND_ASSIGN(FooClass);
-
};
-
// In your source file:
-
FooClass* FooClass::GetInstance() {
-
return Singleton<FooClass>::get();
-
}
-
// And to call methods on FooClass:
-
FooClass::GetInstance()->Bar();
需要注意的是:
- Singleton::get() 的調用方法必須命名為 GetInstance;
- GetInstance 不能是 inline 的,也就是說它的實作不能放在頭檔案裡面;
- Singleton::get() 有一定的時間開銷,避免在循環的 inner loop 裡面每次都調用;
- 對于 CAW 來說,使用 Singleton 的類型的析構函數是不會被自動調用的,對于 Chrome for Android 來說,在子程序退出時,使用 Singleton 的類型的析構函數在程序退出時被自動被調用,另外 LazyInstance 的狀況也一樣;
總的來說,Chromium 并不鼓勵使用單例模式,是以能不用還是不用。
AutoReset
base::AutoReset 是一個很簡單的輔助類,它一般作為棧對象使用,用途是構造時儲存變量原有的值并設定新的值,當生命周期結束,析構的時候恢複變量原有的值。
-
{
-
base::AutoReset<bool> frame_resetter(&viewport_clip_valid_for_dcheck_,
-
true);
-
layer_tree_host_->SetNeedsRedrawRect(clip_);
-
layer_tree_host_->Composite(gfx::FrameTime::Now());
-
}
ObserverList,ObserverListThreadSafe
base::ObserverList 是幫助實作觀察者模式的一個輔助類,顧名思義,它提供了一個觀察者清單容器。除此以外,使用 ObserverList 而不是直接使用 std::vector 或者 std::list 的原因還在于 ObserverList 提供了一個特定版本的疊代器實作,在疊代的過程中從容器中删除自己或者其它的 Observer 是安全的,疊代器的 GetNext 方法會自動檢查容器是否被修改過,正确傳回修改過後的容器的下一個元素。
一般的使用方式如下:
-
class MyWidget {
-
public:
-
...
-
class Observer {
-
public:
-
virtual void OnFoo(MyWidget* w) = 0;
-
virtual void OnBar(MyWidget* w, int x, int y) = 0;
-
};
-
void AddObserver(Observer* obs) {
-
observer_list_.AddObserver(obs);
-
}
-
void RemoveObserver(Observer* obs) {
-
observer_list_.RemoveObserver(obs);
-
}
-
void NotifyFoo() {
-
FOR_EACH_OBSERVER(Observer, observer_list_, OnFoo(this));
-
}
-
void NotifyBar(int x, int y) {
-
FOR_EACH_OBSERVER(Observer, observer_list_, OnBar(this, x, y));
-
}
-
private:
-
ObserverList<Observer> observer_list_;
-
};
base::ObserverListThreadSafe 相當于 base::ObserverList 的線程安全版本,通過 ObserverListThreadSafe.Notify 可以調用注冊的 Observer 的某一個指定的方法,并且這個方法是在這個 Observer 所屬的線程上被調用,所謂 Observer 所屬的線程就是指将 Observer 加入到 ObserverListThreadSafe 裡面的那個調用線程。為了做到上述這一點,ObserverListThreadSafe 是通過 PostTask 到線程的消息循環來實作的,這也意味着跟 ObserverList 不同的是,Notify 和 Callback 被調用是異步的,而 ObserverList 是同步的,MemoryPressureListener 的内部實作就使用了 ObserverListThreadSafe。
-
MemoryPressureListener::MemoryPressureListener(
-
const MemoryPressureListener::MemoryPressureCallback& callback)
-
: callback_(callback) {
-
g_observers.Get().AddObserver(this);
-
}
-
MemoryPressureListener::~MemoryPressureListener() {
-
g_observers.Get().RemoveObserver(this);
-
}
-
void MemoryPressureListener::Notify(MemoryPressureLevel memory_pressure_level) {
-
callback_.Run(memory_pressure_level);
-
}
-
// static
-
void MemoryPressureListener::NotifyMemoryPressure(
-
MemoryPressureLevel memory_pressure_level) {
-
TRACE_EVENT1("memory", "MemoryPressureListener::NotifyMemoryPressure",
-
"level", memory_pressure_level);
-
g_observers.Get().Notify(&MemoryPressureListener::Notify,
-
memory_pressure_level);
-
}
wtf::String to std::string
std::string Ascii() const WARN_UNUSED_RESULT;
std::string Latin1() const WARN_UNUSED_RESULT;
std::string Utf8(UTF8ConversionMode = kLenientUTF8Conversion) const
sdt::string to wtf::String
std::string output{"aaa"};
wtf::String skp(output.data(), output.size());
1.KURL:是WTF::String
char * url= KURL.string().utf8().data();
And how does it come out?
a KURL is defined in WebCore/Platform/KURL.h, it has a member function string().
b KURL.string(), return a class of String, which was defined in wtf/text/WTFString.h, it has a member function utf8()
c String.utf8() return a class of CString , which was defined in wtf/text/CString.h, it has a member function data().
d CString.data() return the type of char* , which can be printed directly.
to be continued
2. String in WTF
a. StringImplBase
defined in "WTF/text/StringImplBase.h"
it has no concrete implementation
b.StringImpl
defined in "WTF/text/StringImpl.h"
StringImplBase<--StringImpl
it hold a member UChar (wchar_t) //16 or 32 bit.
c.String
defined in "WTF/text/WTFString.h"
String hold a member of "StringImpl".
it has function to return it's data in CString
- CString ascii() const;
- CString latin1() const;
- CString utf8(bool strict = false) const;
d. CString
was defined in "WTF/text/CString.h"
it has a function "data()" to return the type of "char*".
how is the UChar come to char *
the most imporant function was String.utf8() in "WTF/text/WTFString.h"
using "convertUTF16ToUTF8". //so UChar is 16 bit.
which was defined in "WTF/unicode/utf8.cpp"
- ConversionResult convertUTF16ToUTF8(
- const UChar** sourceStart, const UChar* sourceEnd,
- char** targetStart, char* targetEnd, bool strict)
- {
- ConversionResult result = conversionOK;
- const UChar* source = *sourceStart;
- char* target = *targetStart;
- while (source < sourceEnd) {
- UChar32 ch;
- unsigned short bytesToWrite = 0;
- const UChar32 byteMask = 0xBF;
- const UChar32 byteMark = 0x80;
- const UChar* oldSource = source; // In case we have to back up because of
- //target overflow.
- ch = static_cast<unsigned short>(*source++);
- // If we have a surrogate pair, convert to UChar32 first.
- if (ch >= 0xD800 && ch <= 0xDBFF) {
- // If the 16 bits following the high surrogate are in the source buffer...
- if (source < sourceEnd) {
- UChar32 ch2 = static_cast<unsigned short>(*source);
- // If it's a low surrogate, convert to UChar32.
- if (ch2 >= 0xDC00 && ch2 <= 0xDFFF) {
- ch = ((ch - 0xD800) << 10) + (ch2 - 0xDC00) + 0x0010000;
- ++source;
- } else if (strict) { // it's an unpaired high surrogate
- --source; // return to the illegal value itself
- result = sourceIllegal;
- break;
- }
- } else { // We don't have the 16 bits following the high surrogate.
- --source; // return to the high surrogate
- result = sourceExhausted;
- break;
- }
- } else if (strict) {
- // UTF-16 surrogate values are illegal in UTF-32
- if (ch >= 0xDC00 && ch <= 0xDFFF) {
- --source; // return to the illegal value itself
- result = sourceIllegal;
- }
- // Figure out how many bytes the result will require
- if (ch < (UChar32)0x80) {
- bytesToWrite = 1;
- } else if (ch < (UChar32)0x800) {
- bytesToWrite = 2;
- } else if (ch < (UChar32)0x10000) {
- bytesToWrite = 3;
- } else if (ch < (UChar32)0x110000) {
- bytesToWrite = 4;
- } else {
- ch = 0xFFFD;
- target += bytesToWrite;
- if (target > targetEnd) {
- source = oldSource; // Back up source pointer!
- target -= bytesToWrite;
- result = targetExhausted;
- break;
- switch (bytesToWrite) { // note: everything falls through.
- case 4: *--target = (char)((ch | byteMark) & byteMask); ch >>= 6;
- case 3: *--target = (char)((ch | byteMark) & byteMask); ch >>= 6;
- case 2: *--target = (char)((ch | byteMark) & byteMask); ch >>= 6;
- case 1: *--target = (char)(ch | firstByteMark[bytesToWrite]);
- }
- *sourceStart = source;
- *targetStart = target;
- return result;
- }
so the most common type used in webcore is "UChar",
the most common type we use to print is "char*"
the most common way for the conversion is "UChar->utf8->char*".
definition
- ConversionResult convertUTF8ToUTF16(
- const char** sourceStart, const char* sourceEnd,
- UChar** targetStart, UChar* targetEnd, bool strict = true);
- ConversionResult convertUTF16ToUTF8(
- const UChar** sourceStart, const UChar* sourceEnd,
- char** targetStart, char* targetEnd, bool strict = true);
example:
- ConversionResult result = convertUTF16ToUTF8(&characters, characters + length,
- &buffer, buffer + bufferVector.size(), strict);
for 2.3
- #include "PlatformString.h"
- #include "CString.h"
to include the head file