1//===- VersionTuple.cpp - Version Number Handling ---------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file implements the VersionTuple class, which represents a version in
10// the form major[.minor[.subminor]].
11//
12//===----------------------------------------------------------------------===//
13
14#include "llvm/Support/VersionTuple.h"
15#include "llvm/ADT/StringRef.h"
16#include "llvm/Support/raw_ostream.h"
17#include <cassert>
18
19using namespace llvm;
20
21std::string VersionTuple::getAsString() const {
22 std::string Result;
23 {
24 llvm::raw_string_ostream Out(Result);
25 Out << *this;
26 }
27 return Result;
28}
29
30raw_ostream &llvm::operator<<(raw_ostream &Out, const VersionTuple &V) {
31 Out << V.getMajor();
32 if (std::optional<unsigned> Minor = V.getMinor())
33 Out << '.' << *Minor;
34 if (std::optional<unsigned> Subminor = V.getSubminor())
35 Out << '.' << *Subminor;
36 if (std::optional<unsigned> Build = V.getBuild())
37 Out << '.' << *Build;
38 return Out;
39}
40
41static bool parseInt(StringRef &input, unsigned &value) {
42 assert(value == 0);
43 if (input.empty())
44 return true;
45
46 char next = input[0];
47 input = input.substr(Start: 1);
48 if (next < '0' || next > '9')
49 return true;
50 value = (unsigned)(next - '0');
51
52 while (!input.empty()) {
53 next = input[0];
54 if (next < '0' || next > '9')
55 return false;
56 input = input.substr(Start: 1);
57 value = value * 10 + (unsigned)(next - '0');
58 }
59
60 return false;
61}
62
63bool VersionTuple::tryParse(StringRef input) {
64 unsigned major = 0, minor = 0, micro = 0, build = 0;
65
66 // Parse the major version, [0-9]+
67 if (parseInt(input, value&: major))
68 return true;
69
70 if (input.empty()) {
71 *this = VersionTuple(major);
72 return false;
73 }
74
75 // If we're not done, parse the minor version, \.[0-9]+
76 if (input[0] != '.')
77 return true;
78 input = input.substr(Start: 1);
79 if (parseInt(input, value&: minor))
80 return true;
81
82 if (input.empty()) {
83 *this = VersionTuple(major, minor);
84 return false;
85 }
86
87 // If we're not done, parse the micro version, \.[0-9]+
88 if (!input.consume_front(Prefix: "."))
89 return true;
90 if (parseInt(input, value&: micro))
91 return true;
92
93 if (input.empty()) {
94 *this = VersionTuple(major, minor, micro);
95 return false;
96 }
97
98 // If we're not done, parse the micro version, \.[0-9]+
99 if (!input.consume_front(Prefix: "."))
100 return true;
101 if (parseInt(input, value&: build))
102 return true;
103
104 // If we have characters left over, it's an error.
105 if (!input.empty())
106 return true;
107
108 *this = VersionTuple(major, minor, micro, build);
109 return false;
110}
111