1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 // Notes on thread-safety: All of the classes here are thread-compatible.  More
18 // specifically, the registry machinery is thread-safe, as long as each thread
19 // performs feature extraction on a different Sentence object.
20 
21 #ifndef NLP_SAFT_COMPONENTS_COMMON_MOBILE_FEL_WORKSPACE_H_
22 #define NLP_SAFT_COMPONENTS_COMMON_MOBILE_FEL_WORKSPACE_H_
23 
24 #include <stddef.h>
25 #include <string>
26 #include <unordered_map>
27 #include <utility>
28 #include <vector>
29 
30 #include "lang_id/common/lite_base/logging.h"
31 #include "lang_id/common/lite_base/macros.h"
32 
33 namespace libtextclassifier3 {
34 namespace mobile {
35 
36 // A base class for shared workspaces. Derived classes implement a static member
37 // function TypeName() which returns a human readable string name for the class.
38 class Workspace {
39  public:
40   // Polymorphic destructor.
~Workspace()41   virtual ~Workspace() {}
42 
43  protected:
44   // Create an empty workspace.
Workspace()45   Workspace() {}
46 
47  private:
48   SAFTM_DISALLOW_COPY_AND_ASSIGN(Workspace);
49 };
50 
51 // Returns a new, strictly increasing int every time it is invoked.
52 int GetFreshTypeId();
53 
54 // Struct to simulate typeid, but without RTTI.
55 template <typename T>
56 struct TypeId {
57   static int type_id;
58 };
59 
60 template <typename T>
61 int TypeId<T>::type_id = GetFreshTypeId();
62 
63 // A registry that keeps track of workspaces.
64 class WorkspaceRegistry {
65  public:
66   // Create an empty registry.
WorkspaceRegistry()67   WorkspaceRegistry() {}
68 
69   // Returns the index of a named workspace, adding it to the registry first
70   // if necessary.
71   template <class W>
Request(const string & name)72   int Request(const string &name) {
73     const int id = TypeId<W>::type_id;
74     max_workspace_id_ = std::max(id, max_workspace_id_);
75     workspace_types_[id] = W::TypeName();
76     std::vector<string> &names = workspace_names_[id];
77     for (int i = 0; i < names.size(); ++i) {
78       if (names[i] == name) return i;
79     }
80     names.push_back(name);
81     return names.size() - 1;
82   }
83 
84   // Returns the maximum workspace id that has been registered.
MaxId()85   int MaxId() const {
86     return max_workspace_id_;
87   }
88 
WorkspaceNames()89   const std::unordered_map<int, std::vector<string> > &WorkspaceNames()
90       const {
91     return workspace_names_;
92   }
93 
94   // Returns a string describing the registered workspaces.
95   string DebugString() const;
96 
97  private:
98   // Workspace type names, indexed as workspace_types_[typeid].
99   std::unordered_map<int, string> workspace_types_;
100 
101   // Workspace names, indexed as workspace_names_[typeid][workspace].
102   std::unordered_map<int, std::vector<string> > workspace_names_;
103 
104   // The maximum workspace id that has been registered.
105   int max_workspace_id_ = 0;
106 
107   SAFTM_DISALLOW_COPY_AND_ASSIGN(WorkspaceRegistry);
108 };
109 
110 // A typed collected of workspaces. The workspaces are indexed according to an
111 // external WorkspaceRegistry. If the WorkspaceSet is const, the contents are
112 // also immutable.
113 class WorkspaceSet {
114  public:
~WorkspaceSet()115   ~WorkspaceSet() { Reset(WorkspaceRegistry()); }
116 
117   // Returns true if a workspace has been set.
118   template <class W>
Has(int index)119   bool Has(int index) const {
120     const int id = TypeId<W>::type_id;
121     SAFTM_DCHECK_GE(id, 0);
122     SAFTM_DCHECK_LT(id, workspaces_.size());
123     SAFTM_DCHECK_GE(index, 0);
124     SAFTM_DCHECK_LT(index, workspaces_[id].size());
125     if (id >= workspaces_.size()) return false;
126     return workspaces_[id][index] != nullptr;
127   }
128 
129   // Returns an indexed workspace; the workspace must have been set.
130   template <class W>
Get(int index)131   const W &Get(int index) const {
132     SAFTM_DCHECK(Has<W>(index));
133     const int id = TypeId<W>::type_id;
134     const Workspace *w = workspaces_[id][index];
135     return reinterpret_cast<const W &>(*w);
136   }
137 
138   // Sets an indexed workspace; this takes ownership of the workspace, which
139   // must have been new-allocated.  It is an error to set a workspace twice.
140   template <class W>
Set(int index,W * workspace)141   void Set(int index, W *workspace) {
142     const int id = TypeId<W>::type_id;
143     SAFTM_DCHECK_GE(id, 0);
144     SAFTM_DCHECK_LT(id, workspaces_.size());
145     SAFTM_DCHECK_GE(index, 0);
146     SAFTM_DCHECK_LT(index, workspaces_[id].size());
147     SAFTM_DCHECK(workspaces_[id][index] == nullptr);
148     SAFTM_DCHECK(workspace != nullptr);
149     workspaces_[id][index] = workspace;
150   }
151 
Reset(const WorkspaceRegistry & registry)152   void Reset(const WorkspaceRegistry &registry) {
153     // Deallocate current workspaces.
154     for (auto &it : workspaces_) {
155       for (size_t index = 0; index < it.size(); ++index) {
156         delete it[index];
157       }
158     }
159     workspaces_.clear();
160     workspaces_.resize(registry.MaxId() + 1, std::vector<Workspace *>());
161     for (auto &it : registry.WorkspaceNames()) {
162       workspaces_[it.first].resize(it.second.size());
163     }
164   }
165 
166  private:
167   // The set of workspaces, indexed as workspaces_[typeid][index].
168   std::vector<std::vector<Workspace *> > workspaces_;
169 };
170 
171 // A workspace that wraps around a vector of int.
172 class VectorIntWorkspace : public Workspace {
173  public:
174   // Creates a vector of the given size.
175   explicit VectorIntWorkspace(int size);
176 
177   // Creates a vector initialized with the given array.
178   explicit VectorIntWorkspace(const std::vector<int> &elements);
179 
180   // Creates a vector of the given size, with each element initialized to the
181   // given value.
182   VectorIntWorkspace(int size, int value);
183 
184   // Returns the name of this type of workspace.
185   static string TypeName();
186 
187   // Returns the i'th element.
element(int i)188   int element(int i) const { return elements_[i]; }
189 
190   // Sets the i'th element.
set_element(int i,int value)191   void set_element(int i, int value) { elements_[i] = value; }
192 
193   // Returns the size of the underlying vector.
size()194   int size() const { return elements_.size(); }
195 
196  private:
197   // The enclosed vector.
198   std::vector<int> elements_;
199 };
200 
201 }  // namespace mobile
202 }  // namespace nlp_saft
203 
204 #endif  // NLP_SAFT_COMPONENTS_COMMON_MOBILE_FEL_WORKSPACE_H_
205