blob: cfc096728f20acbcc0a0b8a72ae7d76a6a06ca54 [file] [log] [blame]
Igor Murashkinaaebaa02015-01-26 10:55:53 -08001/*
2 * Copyright (C) 2015 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#ifndef ART_CMDLINE_CMDLINE_PARSER_H_
18#define ART_CMDLINE_CMDLINE_PARSER_H_
19
20#define CMDLINE_NDEBUG 1 // Do not output any debugging information for parsing.
21
22#include "cmdline/detail/cmdline_parser_detail.h"
23#include "cmdline/detail/cmdline_parse_argument_detail.h"
24#include "cmdline/detail/cmdline_debug_detail.h"
25
26#include "cmdline_type_parser.h"
27#include "token_range.h"
28#include "cmdline_types.h"
29#include "cmdline_result.h"
30#include "cmdline_parse_result.h"
31
32#include "runtime/base/variant_map.h"
Igor Murashkinaaebaa02015-01-26 10:55:53 -080033
34#include <vector>
35#include <memory>
36
37namespace art {
38// Build a parser for command line arguments with a small domain specific language.
39// Each parsed type must have a specialized CmdlineType<T> in order to do the string->T parsing.
40// Each argument must also have a VariantMap::Key<T> in order to do the T storage.
41template <typename TVariantMap,
42 template <typename TKeyValue> class TVariantMapKey>
43struct CmdlineParser {
44 template <typename TArg>
45 struct ArgumentBuilder;
46
47 struct Builder; // Build the parser.
48 struct UntypedArgumentBuilder; // Build arguments which weren't yet given a type.
49
50 private:
51 // Forward declare some functions that we need to use before fully-defining structs.
52 template <typename TArg>
53 static ArgumentBuilder<TArg> CreateArgumentBuilder(Builder& parent);
54 static void AppendCompletedArgument(Builder& builder, detail::CmdlineParseArgumentAny* arg);
55
56 // Allow argument definitions to save their values when they are parsed,
57 // without having a dependency on CmdlineParser or any of the builders.
58 //
59 // A shared pointer to the save destination is saved into the load/save argument callbacks.
60 //
61 // This also allows the underlying storage (i.e. a variant map) to be released
62 // to the user, without having to recreate all of the callbacks.
63 struct SaveDestination {
64 SaveDestination() : variant_map_(new TVariantMap()) {}
65
66 // Save value to the variant map.
67 template <typename TArg>
68 void SaveToMap(const TVariantMapKey<TArg>& key, TArg& value) {
69 variant_map_->Set(key, value);
70 }
71
72 // Get the existing value from a map, creating the value if it did not already exist.
73 template <typename TArg>
74 TArg& GetOrCreateFromMap(const TVariantMapKey<TArg>& key) {
75 auto* ptr = variant_map_->Get(key);
76 if (ptr == nullptr) {
77 variant_map_->Set(key, TArg());
78 ptr = variant_map_->Get(key);
79 assert(ptr != nullptr);
80 }
81
82 return *ptr;
83 }
84
85 protected:
86 // Release the map, clearing it as a side-effect.
87 // Future saves will be distinct from previous saves.
88 TVariantMap&& ReleaseMap() {
89 return std::move(*variant_map_);
90 }
91
92 // Get a read-only reference to the variant map.
93 const TVariantMap& GetMap() {
94 return *variant_map_;
95 }
96
97 // Clear all potential save targets.
98 void Clear() {
99 variant_map_->Clear();
100 }
101
102 private:
103 // Don't try to copy or move this. Just don't.
104 SaveDestination(const SaveDestination&) = delete;
105 SaveDestination(SaveDestination&&) = delete;
106 SaveDestination& operator=(const SaveDestination&) = delete;
107 SaveDestination& operator=(SaveDestination&&) = delete;
108
109 std::shared_ptr<TVariantMap> variant_map_;
110
111 // Allow the parser to change the underlying pointers when we release the underlying storage.
112 friend struct CmdlineParser;
113 };
114
115 public:
116 // Builder for the argument definition of type TArg. Do not use this type directly,
117 // it is only a separate type to provide compile-time enforcement against doing
118 // illegal builds.
119 template <typename TArg>
120 struct ArgumentBuilder {
121 // Add a range check to this argument.
122 ArgumentBuilder<TArg>& WithRange(const TArg& min, const TArg& max) {
123 argument_info_.has_range_ = true;
124 argument_info_.min_ = min;
125 argument_info_.max_ = max;
126
127 return *this;
128 }
129
130 // Map the list of names into the list of values. List of names must not have
131 // any wildcards '_' in it.
132 //
133 // Do not use if a value map has already been set.
134 ArgumentBuilder<TArg>& WithValues(std::initializer_list<TArg> value_list) {
135 SetValuesInternal(value_list);
136 return *this;
137 }
138
139 // When used with a single alias, map the alias into this value.
140 // Same as 'WithValues({value})' , but allows the omission of the curly braces {}.
141 ArgumentBuilder<TArg> WithValue(const TArg& value) {
142 return WithValues({ value });
143 }
144
145 // Map the parsed string values (from _) onto a concrete value. If no wildcard
146 // has been specified, then map the value directly from the arg name (i.e.
147 // if there are multiple aliases, then use the alias to do the mapping).
148 //
149 // Do not use if a values list has already been set.
150 ArgumentBuilder<TArg>& WithValueMap(
151 std::initializer_list<std::pair<const char*, TArg>> key_value_list) {
152 assert(!argument_info_.has_value_list_);
153
154 argument_info_.has_value_map_ = true;
155 argument_info_.value_map_ = key_value_list;
156
157 return *this;
158 }
159
160 // If this argument is seen multiple times, successive arguments mutate the same value
161 // instead of replacing it with a new value.
162 ArgumentBuilder<TArg>& AppendValues() {
163 argument_info_.appending_values_ = true;
164
165 return *this;
166 }
167
168 // Convenience type alias for the variant map key type definition.
169 using MapKey = TVariantMapKey<TArg>;
170
171 // Write the results of this argument into the key.
172 // To look up the parsed arguments, get the map and then use this key with VariantMap::Get
173 CmdlineParser::Builder& IntoKey(const MapKey& key) {
174 // Only capture save destination as a pointer.
175 // This allows the parser to later on change the specific save targets.
176 auto save_destination = save_destination_;
177 save_value_ = [save_destination, &key](TArg& value) {
178 save_destination->SaveToMap(key, value);
179 CMDLINE_DEBUG_LOG << "Saved value into map '"
180 << detail::ToStringAny(value) << "'" << std::endl;
181 };
182
183 load_value_ = [save_destination, &key]() -> TArg& {
184 TArg& value = save_destination->GetOrCreateFromMap(key);
185 CMDLINE_DEBUG_LOG << "Loaded value from map '" << detail::ToStringAny(value) << "'"
186 << std::endl;
187
188 return value;
189 };
190
191 save_value_specified_ = true;
192 load_value_specified_ = true;
193
194 CompleteArgument();
195 return parent_;
196 }
197
198 // Ensure we always move this when returning a new builder.
199 ArgumentBuilder(ArgumentBuilder&&) = default;
200
201 protected:
202 // Used by builder to internally ignore arguments by dropping them on the floor after parsing.
203 CmdlineParser::Builder& IntoIgnore() {
204 save_value_ = [](TArg& value) {
205 CMDLINE_DEBUG_LOG << "Ignored value '" << detail::ToStringAny(value) << "'" << std::endl;
206 };
207 load_value_ = []() -> TArg& {
208 assert(false && "Should not be appending values to ignored arguments");
209 return *reinterpret_cast<TArg*>(0); // Blow up.
210 };
211
212 save_value_specified_ = true;
213 load_value_specified_ = true;
214
215 CompleteArgument();
216 return parent_;
217 }
218
219 void SetValuesInternal(const std::vector<TArg>&& value_list) {
220 assert(!argument_info_.has_value_map_);
221
222 argument_info_.has_value_list_ = true;
223 argument_info_.value_list_ = value_list;
224 }
225
226 void SetNames(std::vector<const char*>&& names) {
227 argument_info_.names_ = names;
228 }
229
230 void SetNames(std::initializer_list<const char*> names) {
231 argument_info_.names_ = names;
232 }
233
234 private:
235 // Copying is bad. Move only.
236 ArgumentBuilder(const ArgumentBuilder&) = delete;
237
238 // Called by any function that doesn't chain back into this builder.
239 // Completes the argument builder and save the information into the main builder.
240 void CompleteArgument() {
241 assert(save_value_specified_ &&
242 "No Into... function called, nowhere to save parsed values to");
243 assert(load_value_specified_ &&
244 "No Into... function called, nowhere to load parsed values from");
245
246 argument_info_.CompleteArgument();
247
248 // Appending the completed argument is destructive. The object is no longer
249 // usable since all the useful information got moved out of it.
250 AppendCompletedArgument(parent_,
251 new detail::CmdlineParseArgument<TArg>(
252 std::move(argument_info_),
253 std::move(save_value_),
254 std::move(load_value_)));
255 }
256
257 friend struct CmdlineParser;
258 friend struct CmdlineParser::Builder;
259 friend struct CmdlineParser::UntypedArgumentBuilder;
260
261 ArgumentBuilder(CmdlineParser::Builder& parser,
262 std::shared_ptr<SaveDestination> save_destination)
263 : parent_(parser),
264 save_value_specified_(false),
265 load_value_specified_(false),
266 save_destination_(save_destination) {
267 save_value_ = [](TArg&) {
268 assert(false && "No save value function defined");
269 };
270
271 load_value_ = []() -> TArg& {
272 assert(false && "No load value function defined");
273 return *reinterpret_cast<TArg*>(0); // Blow up.
274 };
275 }
276
277 CmdlineParser::Builder& parent_;
278 std::function<void(TArg&)> save_value_;
279 std::function<TArg&(void)> load_value_;
280 bool save_value_specified_;
281 bool load_value_specified_;
282 detail::CmdlineParserArgumentInfo<TArg> argument_info_;
283
284 std::shared_ptr<SaveDestination> save_destination_;
285 };
286
287 struct UntypedArgumentBuilder {
288 // Set a type for this argument. The specific subcommand parser is looked up by the type.
289 template <typename TArg>
290 ArgumentBuilder<TArg> WithType() {
291 return CreateTypedBuilder<TArg>();
292 }
293
294 // When used with multiple aliases, map the position of the alias to the value position.
295 template <typename TArg>
296 ArgumentBuilder<TArg> WithValues(std::initializer_list<TArg> values) {
297 auto&& a = CreateTypedBuilder<TArg>();
298 a.WithValues(values);
299 return std::move(a);
300 }
301
302 // When used with a single alias, map the alias into this value.
303 // Same as 'WithValues({value})' , but allows the omission of the curly braces {}.
304 template <typename TArg>
305 ArgumentBuilder<TArg> WithValue(const TArg& value) {
306 return WithValues({ value });
307 }
308
309 // Set the current building argument to target this key.
310 // When this command line argument is parsed, it can be fetched with this key.
311 Builder& IntoKey(const TVariantMapKey<Unit>& key) {
312 return CreateTypedBuilder<Unit>().IntoKey(key);
313 }
314
315 // Ensure we always move this when returning a new builder.
316 UntypedArgumentBuilder(UntypedArgumentBuilder&&) = default;
317
318 protected:
319 void SetNames(std::vector<const char*>&& names) {
320 names_ = std::move(names);
321 }
322
323 void SetNames(std::initializer_list<const char*> names) {
324 names_ = names;
325 }
326
327 private:
328 // No copying. Move instead.
329 UntypedArgumentBuilder(const UntypedArgumentBuilder&) = delete;
330
331 template <typename TArg>
332 ArgumentBuilder<TArg> CreateTypedBuilder() {
333 auto&& b = CreateArgumentBuilder<TArg>(parent_);
334 InitializeTypedBuilder(&b); // Type-specific initialization
335 b.SetNames(std::move(names_));
336 return std::move(b);
337 }
338
339 template <typename TArg = Unit>
340 typename std::enable_if<std::is_same<TArg, Unit>::value>::type
341 InitializeTypedBuilder(ArgumentBuilder<TArg>* arg_builder) {
342 // Every Unit argument implicitly maps to a runtime value of Unit{}
343 std::vector<Unit> values(names_.size(), Unit{}); // NOLINT [whitespace/braces] [5]
344 arg_builder->SetValuesInternal(std::move(values));
345 }
346
347 // No extra work for all other types
348 void InitializeTypedBuilder(void*) {}
349
350 template <typename TArg>
351 friend struct ArgumentBuilder;
352 friend struct Builder;
353
354 explicit UntypedArgumentBuilder(CmdlineParser::Builder& parent) : parent_(parent) {}
355 // UntypedArgumentBuilder(UntypedArgumentBuilder&& other) = default;
356
357 CmdlineParser::Builder& parent_;
358 std::vector<const char*> names_;
359 };
360
361 // Build a new parser given a chain of calls to define arguments.
362 struct Builder {
363 Builder() : save_destination_(new SaveDestination()) {}
364
365 // Define a single argument. The default type is Unit.
366 UntypedArgumentBuilder Define(const char* name) {
367 return Define({name});
368 }
369
370 // Define a single argument with multiple aliases.
371 UntypedArgumentBuilder Define(std::initializer_list<const char*> names) {
372 auto&& b = UntypedArgumentBuilder(*this);
373 b.SetNames(names);
374 return std::move(b);
375 }
376
377 // Whether the parser should give up on unrecognized arguments. Not recommended.
378 Builder& IgnoreUnrecognized(bool ignore_unrecognized) {
379 ignore_unrecognized_ = ignore_unrecognized;
380 return *this;
381 }
382
383 // Provide a list of arguments to ignore for backwards compatibility.
384 Builder& Ignore(std::initializer_list<const char*> ignore_list) {
385 for (auto&& ignore_name : ignore_list) {
386 std::string ign = ignore_name;
387
388 // Ignored arguments are just like a regular definition which have very
389 // liberal parsing requirements (no range checks, no value checks).
390 // Unlike regular argument definitions, when a value gets parsed into its
391 // stronger type, we just throw it away.
392
393 if (ign.find("_") != std::string::npos) { // Does the arg-def have a wildcard?
394 // pretend this is a string, e.g. -Xjitconfig:<anythinggoeshere>
395 auto&& builder = Define(ignore_name).template WithType<std::string>().IntoIgnore();
396 assert(&builder == this);
397 (void)builder; // Ignore pointless unused warning, it's used in the assert.
398 } else {
399 // pretend this is a unit, e.g. -Xjitblocking
400 auto&& builder = Define(ignore_name).template WithType<Unit>().IntoIgnore();
401 assert(&builder == this);
402 (void)builder; // Ignore pointless unused warning, it's used in the assert.
403 }
404 }
405 ignore_list_ = ignore_list;
406 return *this;
407 }
408
409 // Finish building the parser; performs sanity checks. Return value is moved, not copied.
410 // Do not call this more than once.
411 CmdlineParser Build() {
412 assert(!built_);
413 built_ = true;
414
415 auto&& p = CmdlineParser(ignore_unrecognized_,
416 std::move(ignore_list_),
417 save_destination_,
418 std::move(completed_arguments_));
419
420 return std::move(p);
421 }
422
423 protected:
424 void AppendCompletedArgument(detail::CmdlineParseArgumentAny* arg) {
425 auto smart_ptr = std::unique_ptr<detail::CmdlineParseArgumentAny>(arg);
426 completed_arguments_.push_back(std::move(smart_ptr));
427 }
428
429 private:
430 // No copying now!
431 Builder(const Builder& other) = delete;
432
433 template <typename TArg>
434 friend struct ArgumentBuilder;
435 friend struct UntypedArgumentBuilder;
436 friend struct CmdlineParser;
437
438 bool built_ = false;
439 bool ignore_unrecognized_ = false;
440 std::vector<const char*> ignore_list_;
441 std::shared_ptr<SaveDestination> save_destination_;
442
443 std::vector<std::unique_ptr<detail::CmdlineParseArgumentAny>> completed_arguments_;
444 };
445
446 CmdlineResult Parse(const std::string& argv) {
447 std::vector<std::string> tokenized;
448 Split(argv, ' ', &tokenized);
449
450 return Parse(TokenRange(std::move(tokenized)));
451 }
452
453 // Parse the arguments; storing results into the arguments map. Returns success value.
454 CmdlineResult Parse(const char* argv) {
455 return Parse(std::string(argv));
456 }
457
458 // Parse the arguments; storing the results into the arguments map. Returns success value.
459 // Assumes that argv[0] is a valid argument (i.e. not the program name).
460 CmdlineResult Parse(const std::vector<const char*>& argv) {
461 return Parse(TokenRange(argv.begin(), argv.end()));
462 }
463
464 // Parse the arguments; storing the results into the arguments map. Returns success value.
465 // Assumes that argv[0] is a valid argument (i.e. not the program name).
466 CmdlineResult Parse(const std::vector<std::string>& argv) {
467 return Parse(TokenRange(argv.begin(), argv.end()));
468 }
469
470 // Parse the arguments (directly from an int main(argv,argc)). Returns success value.
471 // Assumes that argv[0] is the program name, and ignores it.
472 CmdlineResult Parse(const char* argv[], int argc) {
473 return Parse(TokenRange(&argv[1], argc - 1)); // ignore argv[0] because it's the program name
474 }
475
476 // Look up the arguments that have been parsed; use the target keys to lookup individual args.
477 const TVariantMap& GetArgumentsMap() const {
478 return save_destination_->GetMap();
479 }
480
481 // Release the arguments map that has been parsed; useful for move semantics.
482 TVariantMap&& ReleaseArgumentsMap() {
483 return save_destination_->ReleaseMap();
484 }
485
486 // How many arguments were defined?
487 size_t CountDefinedArguments() const {
488 return completed_arguments_.size();
489 }
490
491 // Ensure we have a default move constructor.
Andreas Gampec801f0d2015-02-24 20:55:16 -0800492 CmdlineParser(CmdlineParser&&) = default;
Igor Murashkinaaebaa02015-01-26 10:55:53 -0800493 // Ensure we have a default move assignment operator.
Andreas Gampec801f0d2015-02-24 20:55:16 -0800494 CmdlineParser& operator=(CmdlineParser&&) = default;
Igor Murashkinaaebaa02015-01-26 10:55:53 -0800495
496 private:
497 friend struct Builder;
498
499 // Construct a new parser from the builder. Move all the arguments.
Roland Levillain3887c462015-08-12 18:15:42 +0100500 CmdlineParser(bool ignore_unrecognized,
501 std::vector<const char*>&& ignore_list,
502 std::shared_ptr<SaveDestination> save_destination,
503 std::vector<std::unique_ptr<detail::CmdlineParseArgumentAny>>&& completed_arguments)
Igor Murashkinaaebaa02015-01-26 10:55:53 -0800504 : ignore_unrecognized_(ignore_unrecognized),
505 ignore_list_(std::move(ignore_list)),
506 save_destination_(save_destination),
507 completed_arguments_(std::move(completed_arguments)) {
508 assert(save_destination != nullptr);
509 }
510
511 // Parse the arguments; storing results into the arguments map. Returns success value.
512 // The parsing will fail on the first non-success parse result and return that error.
513 //
514 // All previously-parsed arguments are cleared out.
515 // Otherwise, all parsed arguments will be stored into SaveDestination as a side-effect.
516 // A partial parse will result only in a partial save of the arguments.
517 CmdlineResult Parse(TokenRange&& arguments_list) {
518 save_destination_->Clear();
519
520 for (size_t i = 0; i < arguments_list.Size(); ) {
521 TokenRange possible_name = arguments_list.Slice(i);
522
523 size_t best_match_size = 0; // How many tokens were matched in the best case.
524 size_t best_match_arg_idx = 0;
525 bool matched = false; // At least one argument definition has been matched?
526
527 // Find the closest argument definition for the remaining token range.
528 size_t arg_idx = 0;
529 for (auto&& arg : completed_arguments_) {
530 size_t local_match = arg->MaybeMatches(possible_name);
531
532 if (local_match > best_match_size) {
533 best_match_size = local_match;
534 best_match_arg_idx = arg_idx;
535 matched = true;
536 }
537 arg_idx++;
538 }
539
540 // Saw some kind of unknown argument
541 if (matched == false) {
542 if (UNLIKELY(ignore_unrecognized_)) { // This is usually off, we only need it for JNI.
543 // Consume 1 token and keep going, hopefully the next token is a good one.
544 ++i;
545 continue;
546 }
547 // Common case:
548 // Bail out on the first unknown argument with an error.
549 return CmdlineResult(CmdlineResult::kUnknown,
550 std::string("Unknown argument: ") + possible_name[0]);
551 }
552
553 // Look at the best-matched argument definition and try to parse against that.
554 auto&& arg = completed_arguments_[best_match_arg_idx];
555
556 assert(arg->MaybeMatches(possible_name) == best_match_size);
557
558 // Try to parse the argument now, if we have enough tokens.
559 std::pair<size_t, size_t> num_tokens = arg->GetNumTokens();
560 size_t min_tokens;
561 size_t max_tokens;
562
563 std::tie(min_tokens, max_tokens) = num_tokens;
564
565 if ((i + min_tokens) > arguments_list.Size()) {
566 // expected longer command line but it was too short
567 // e.g. if the argv was only "-Xms" without specifying a memory option
568 CMDLINE_DEBUG_LOG << "Parse failure, i = " << i << ", arg list " << arguments_list.Size() <<
569 " num tokens in arg_def: " << min_tokens << "," << max_tokens << std::endl;
570 return CmdlineResult(CmdlineResult::kFailure,
571 std::string("Argument ") +
572 possible_name[0] + ": incomplete command line arguments, expected "
573 + std::to_string(size_t(i + min_tokens) - arguments_list.Size()) +
574 " more tokens");
575 }
576
577 if (best_match_size > max_tokens || best_match_size < min_tokens) {
578 // Even our best match was out of range, so parsing would fail instantly.
579 return CmdlineResult(CmdlineResult::kFailure,
580 std::string("Argument ") + possible_name[0] + ": too few tokens "
581 "matched " + std::to_string(best_match_size)
582 + " but wanted " + std::to_string(num_tokens.first));
583 }
584
585 // We have enough tokens to begin exact parsing.
586 TokenRange exact_range = possible_name.Slice(0, max_tokens);
587
588 size_t consumed_tokens = 1; // At least 1 if we ever want to try to resume parsing on error
589 CmdlineResult parse_attempt = arg->ParseArgument(exact_range, &consumed_tokens);
590
591 if (parse_attempt.IsError()) {
592 // We may also want to continue parsing the other tokens to gather more errors.
593 return parse_attempt;
594 } // else the value has been successfully stored into the map
595
596 assert(consumed_tokens > 0); // Don't hang in an infinite loop trying to parse
597 i += consumed_tokens;
598
599 // TODO: also handle ignoring arguments for backwards compatibility
600 } // for
601
602 return CmdlineResult(CmdlineResult::kSuccess);
603 }
604
605 bool ignore_unrecognized_ = false;
606 std::vector<const char*> ignore_list_;
607 std::shared_ptr<SaveDestination> save_destination_;
608 std::vector<std::unique_ptr<detail::CmdlineParseArgumentAny>> completed_arguments_;
609};
610
611// This has to be defined after everything else, since we want the builders to call this.
612template <typename TVariantMap,
613 template <typename TKeyValue> class TVariantMapKey>
614template <typename TArg>
615CmdlineParser<TVariantMap, TVariantMapKey>::ArgumentBuilder<TArg>
616CmdlineParser<TVariantMap, TVariantMapKey>::CreateArgumentBuilder(
617 CmdlineParser<TVariantMap, TVariantMapKey>::Builder& parent) {
618 return CmdlineParser<TVariantMap, TVariantMapKey>::ArgumentBuilder<TArg>(
619 parent, parent.save_destination_);
620}
621
622// This has to be defined after everything else, since we want the builders to call this.
623template <typename TVariantMap,
624 template <typename TKeyValue> class TVariantMapKey>
625void CmdlineParser<TVariantMap, TVariantMapKey>::AppendCompletedArgument(
626 CmdlineParser<TVariantMap, TVariantMapKey>::Builder& builder,
627 detail::CmdlineParseArgumentAny* arg) {
628 builder.AppendCompletedArgument(arg);
629}
630
631} // namespace art
632
633#endif // ART_CMDLINE_CMDLINE_PARSER_H_