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 if (std::optional<unsigned> Subbuild = V.getSubbuild())
39 Out << '.' << *Subbuild;
40 return Out;
41}
42
43static bool parseInt(StringRef &input, unsigned &value) {
44 assert(value == 0);
45 if (input.empty())
46 return true;
47
48 char next = input[0];
49 input = input.substr(Start: 1);
50 if (next < '0' || next > '9')
51 return true;
52 value = (unsigned)(next - '0');
53
54 while (!input.empty()) {
55 next = input[0];
56 if (next < '0' || next > '9')
57 return false;
58 input = input.substr(Start: 1);
59 value = value * 10 + (unsigned)(next - '0');
60 }
61
62 return false;
63}
64
65bool VersionTuple::tryParse(StringRef input) {
66 unsigned major = 0, minor = 0, subminor = 0, build = 0, subbuild = 0;
67
68 // Parse the major version, [0-9]+
69 if (parseInt(input, value&: major))
70 return true;
71
72 if (input.empty()) {
73 *this = VersionTuple(major);
74 return false;
75 }
76
77 // If we're not done, parse the minor version, \.[0-9]+
78 if (input[0] != '.')
79 return true;
80 input = input.substr(Start: 1);
81 if (parseInt(input, value&: minor))
82 return true;
83
84 if (input.empty()) {
85 *this = VersionTuple(major, minor);
86 return false;
87 }
88
89 // If we're not done, parse the subminor version, \.[0-9]+
90 if (!input.consume_front(Prefix: "."))
91 return true;
92 if (parseInt(input, value&: subminor))
93 return true;
94
95 if (input.empty()) {
96 *this = VersionTuple(major, minor, subminor);
97 return false;
98 }
99
100 // If we're not done, parse the build version, \.[0-9]+
101 if (!input.consume_front(Prefix: "."))
102 return true;
103 if (parseInt(input, value&: build))
104 return true;
105 if (build >= 1024 * 1024)
106 return true;
107
108 if (input.empty()) {
109 *this = VersionTuple(major, minor, subminor, build);
110 return false;
111 }
112
113 // And the subbuild version, \.[0-9]+
114 if (!input.consume_front(Prefix: "."))
115 return true;
116 if (parseInt(input, value&: subbuild))
117 return true;
118 if (subbuild >= 1024)
119 return true;
120
121 // If we have characters left over, it's an error.
122 if (!input.empty())
123 return true;
124
125 *this = VersionTuple(major, minor, subminor, build, subbuild);
126 return false;
127}
128
129VersionTuple VersionTuple::withMajorReplaced(unsigned NewMajor) const {
130 if (HasSubbuild)
131 return VersionTuple(NewMajor, Minor, Subminor, Build, Subbuild);
132 if (HasBuild)
133 return VersionTuple(NewMajor, Minor, Subminor, Build);
134 if (HasSubminor)
135 return VersionTuple(NewMajor, Minor, Subminor);
136 if (HasMinor)
137 return VersionTuple(NewMajor, Minor);
138 return VersionTuple(NewMajor);
139}
140