diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000000..6c2ff60b60 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "githubPullRequests.ignoredPullRequestBranches": [ + "master" + ] +} \ No newline at end of file diff --git a/1about.html b/1about.html new file mode 100644 index 0000000000..342a48f0fb --- /dev/null +++ b/1about.html @@ -0,0 +1,27 @@ +--- +layout: page +title: About Me +description: This is what I do. +background: '/img/bg-about.jpg' +--- + +
A Deep Learning enthusiast. Currently working on applying Deep + Learning to Spoken Language Processing. + Energetic Senior Software Engineer with 3+ years of experience + having a diverse skill-set and creative drive to software application + development. Proficient in writing code in various languages. + And having extensive experience in developing customized Deep + learning/Machine Learning architectures from scratch with good + mathematical understanding. + My goal is simple "A complete understanding of how the brain works".
+ +Currently I'm open to PhD/Research position
+ +My resume:
+ + + +Feel free to contact me on
+ email: satish.pas@gmail.com +phone: +918971000639 + diff --git a/1contact.html b/1contact.html new file mode 100644 index 0000000000..65c691ceae --- /dev/null +++ b/1contact.html @@ -0,0 +1,24 @@ +--- +layout: page +title: Contact Me +description: Have questions? I have answers. +background: '/img/bg-contact.jpg' +form: true +--- + + + diff --git a/Research.html b/Research.html new file mode 100644 index 0000000000..be4965fa55 --- /dev/null +++ b/Research.html @@ -0,0 +1,28 @@ + + + +Never in all their history have men been able truly to conceive of the world as one: a single sphere, a globe, having the qualities of a globe, a round earth in which all the directions eventually meet, in which there is no center because every point, or none, is center — an equal earth which all men occupy as equals. The airman's earth, if free men make it, will be truly round: a globe in practice, not in theory.
- -Science cuts two ways, of course; its products can be used for both good and evil. But there's no turning back from science. The early warnings about technological dangers also come from science.
- -What was most significant about the lunar voyage was not that man set foot on the Moon but that they set eye on the earth.
- -A Chinese tale tells of some men sent to harm a young girl who, upon seeing her beauty, become her protectors rather than her violators. That's how I felt seeing the Earth for the first time. I could not help but love and cherish her.
- -For those who have seen the Earth from space, and for the hundreds and perhaps thousands more who will, the experience most certainly changes your perspective. The things that we share in our world are far more valuable than those which divide us.
- -There can be no thought of finishing for ‘aiming for the stars.’ Both figuratively and literally, it is a task to occupy the generations. And no matter how much progress one makes, there is always the thrill of just beginning.
- -There can be no thought of finishing for ‘aiming for the stars.’ Both figuratively and literally, it is a task to occupy the generations. And no matter how much progress one makes, there is always the thrill of just beginning.
- -The dreams of yesterday are the hopes of today and the reality of tomorrow. Science has not yet mastered prophecy. We predict too much for the next year and yet far too little for the next ten.- -
Spaceflights cannot be stopped. This is not the work of any one man or even a group of men. It is a historical process which mankind is carrying out in accordance with the natural laws of human development.
- -As we got further and further away, it [the Earth] diminished in size. Finally it shrank to the size of a marble, the most beautiful you can imagine. That beautiful, warm, living object looked so fragile, so delicate, that if you touched it with a finger it would crumble and fall apart. Seeing this has to change a man.
- -Space, the final frontier. These are the voyages of the Starship Enterprise. Its five-year mission: to explore strange new worlds, to seek out new life and new civilizations, to boldly go where no man has gone before.
- -As I stand out here in the wonders of the unknown at Hadley, I sort of realize there’s a fundamental truth to our nature, Man must explore, and this is exploration at its greatest.
- -Placeholder text by Space Ipsum. Photographs by Unsplash.
diff --git a/_posts/2020-01-27-dreams.html b/_posts/2020-01-27-dreams.html deleted file mode 100644 index f687623ea2..0000000000 --- a/_posts/2020-01-27-dreams.html +++ /dev/null @@ -1,39 +0,0 @@ ---- -layout: post -title: "The dreams of yesterday are the hopes of today and the reality of tomorrow." -date: 2020-01-27 23:45:13 -0400 -background: '/img/posts/02.jpg' ---- - -Never in all their history have men been able truly to conceive of the world as one: a single sphere, a globe, having the qualities of a globe, a round earth in which all the directions eventually meet, in which there is no center because every point, or none, is center — an equal earth which all men occupy as equals. The airman's earth, if free men make it, will be truly round: a globe in practice, not in theory.
- -Science cuts two ways, of course; its products can be used for both good and evil. But there's no turning back from science. The early warnings about technological dangers also come from science.
- -What was most significant about the lunar voyage was not that man set foot on the Moon but that they set eye on the earth.
- -A Chinese tale tells of some men sent to harm a young girl who, upon seeing her beauty, become her protectors rather than her violators. That's how I felt seeing the Earth for the first time. I could not help but love and cherish her.
- -For those who have seen the Earth from space, and for the hundreds and perhaps thousands more who will, the experience most certainly changes your perspective. The things that we share in our world are far more valuable than those which divide us.
- -There can be no thought of finishing for ‘aiming for the stars.’ Both figuratively and literally, it is a task to occupy the generations. And no matter how much progress one makes, there is always the thrill of just beginning.
- -There can be no thought of finishing for ‘aiming for the stars.’ Both figuratively and literally, it is a task to occupy the generations. And no matter how much progress one makes, there is always the thrill of just beginning.
- -The dreams of yesterday are the hopes of today and the reality of tomorrow. Science has not yet mastered prophecy. We predict too much for the next year and yet far too little for the next ten.- -
Spaceflights cannot be stopped. This is not the work of any one man or even a group of men. It is a historical process which mankind is carrying out in accordance with the natural laws of human development.
- -As we got further and further away, it [the Earth] diminished in size. Finally it shrank to the size of a marble, the most beautiful you can imagine. That beautiful, warm, living object looked so fragile, so delicate, that if you touched it with a finger it would crumble and fall apart. Seeing this has to change a man.
- -Space, the final frontier. These are the voyages of the Starship Enterprise. Its five-year mission: to explore strange new worlds, to seek out new life and new civilizations, to boldly go where no man has gone before.
- -As I stand out here in the wonders of the unknown at Hadley, I sort of realize there’s a fundamental truth to our nature, Man must explore, and this is exploration at its greatest.
- -Placeholder text by Space Ipsum. Photographs by Unsplash.
diff --git a/_posts/2020-01-28-exploration.html b/_posts/2020-01-28-exploration.html deleted file mode 100644 index ec94119725..0000000000 --- a/_posts/2020-01-28-exploration.html +++ /dev/null @@ -1,40 +0,0 @@ ---- -layout: post -title: "Failure is not an option" -subtitle: "Many say exploration is part of our destiny, but it’s actually our duty to future generations." -date: 2020-01-28 23:45:13 -0400 -background: '/img/posts/03.jpg' ---- - -Never in all their history have men been able truly to conceive of the world as one: a single sphere, a globe, having the qualities of a globe, a round earth in which all the directions eventually meet, in which there is no center because every point, or none, is center — an equal earth which all men occupy as equals. The airman's earth, if free men make it, will be truly round: a globe in practice, not in theory.
- -Science cuts two ways, of course; its products can be used for both good and evil. But there's no turning back from science. The early warnings about technological dangers also come from science.
- -What was most significant about the lunar voyage was not that man set foot on the Moon but that they set eye on the earth.
- -A Chinese tale tells of some men sent to harm a young girl who, upon seeing her beauty, become her protectors rather than her violators. That's how I felt seeing the Earth for the first time. I could not help but love and cherish her.
- -For those who have seen the Earth from space, and for the hundreds and perhaps thousands more who will, the experience most certainly changes your perspective. The things that we share in our world are far more valuable than those which divide us.
- -There can be no thought of finishing for ‘aiming for the stars.’ Both figuratively and literally, it is a task to occupy the generations. And no matter how much progress one makes, there is always the thrill of just beginning.
- -There can be no thought of finishing for ‘aiming for the stars.’ Both figuratively and literally, it is a task to occupy the generations. And no matter how much progress one makes, there is always the thrill of just beginning.
- -The dreams of yesterday are the hopes of today and the reality of tomorrow. Science has not yet mastered prophecy. We predict too much for the next year and yet far too little for the next ten.- -
Spaceflights cannot be stopped. This is not the work of any one man or even a group of men. It is a historical process which mankind is carrying out in accordance with the natural laws of human development.
- -As we got further and further away, it [the Earth] diminished in size. Finally it shrank to the size of a marble, the most beautiful you can imagine. That beautiful, warm, living object looked so fragile, so delicate, that if you touched it with a finger it would crumble and fall apart. Seeing this has to change a man.
- -Space, the final frontier. These are the voyages of the Starship Enterprise. Its five-year mission: to explore strange new worlds, to seek out new life and new civilizations, to boldly go where no man has gone before.
- -As I stand out here in the wonders of the unknown at Hadley, I sort of realize there’s a fundamental truth to our nature, Man must explore, and this is exploration at its greatest.
- -Placeholder text by Space Ipsum. Photographs by Unsplash.
diff --git a/_posts/2020-01-29-prophecy.html b/_posts/2020-01-29-prophecy.html deleted file mode 100644 index 65704ea439..0000000000 --- a/_posts/2020-01-29-prophecy.html +++ /dev/null @@ -1,40 +0,0 @@ ---- -layout: post -title: "Science has not yet mastered prophecy" -subtitle: "We predict too much for the next year and yet far too little for the next ten." -date: 2020-01-29 23:45:13 -0400 -background: '/img/posts/04.jpg' ---- - -Never in all their history have men been able truly to conceive of the world as one: a single sphere, a globe, having the qualities of a globe, a round earth in which all the directions eventually meet, in which there is no center because every point, or none, is center — an equal earth which all men occupy as equals. The airman's earth, if free men make it, will be truly round: a globe in practice, not in theory.
- -Science cuts two ways, of course; its products can be used for both good and evil. But there's no turning back from science. The early warnings about technological dangers also come from science.
- -What was most significant about the lunar voyage was not that man set foot on the Moon but that they set eye on the earth.
- -A Chinese tale tells of some men sent to harm a young girl who, upon seeing her beauty, become her protectors rather than her violators. That's how I felt seeing the Earth for the first time. I could not help but love and cherish her.
- -For those who have seen the Earth from space, and for the hundreds and perhaps thousands more who will, the experience most certainly changes your perspective. The things that we share in our world are far more valuable than those which divide us.
- -There can be no thought of finishing for ‘aiming for the stars.’ Both figuratively and literally, it is a task to occupy the generations. And no matter how much progress one makes, there is always the thrill of just beginning.
- -There can be no thought of finishing for ‘aiming for the stars.’ Both figuratively and literally, it is a task to occupy the generations. And no matter how much progress one makes, there is always the thrill of just beginning.
- -The dreams of yesterday are the hopes of today and the reality of tomorrow. Science has not yet mastered prophecy. We predict too much for the next year and yet far too little for the next ten.- -
Spaceflights cannot be stopped. This is not the work of any one man or even a group of men. It is a historical process which mankind is carrying out in accordance with the natural laws of human development.
- -As we got further and further away, it [the Earth] diminished in size. Finally it shrank to the size of a marble, the most beautiful you can imagine. That beautiful, warm, living object looked so fragile, so delicate, that if you touched it with a finger it would crumble and fall apart. Seeing this has to change a man.
- -Space, the final frontier. These are the voyages of the Starship Enterprise. Its five-year mission: to explore strange new worlds, to seek out new life and new civilizations, to boldly go where no man has gone before.
- -As I stand out here in the wonders of the unknown at Hadley, I sort of realize there’s a fundamental truth to our nature, Man must explore, and this is exploration at its greatest.
- -Placeholder text by Space Ipsum. Photographs by Unsplash.
diff --git a/_posts/2020-01-30-heartbeats.html b/_posts/2020-01-30-heartbeats.html deleted file mode 100644 index a444e6cd18..0000000000 --- a/_posts/2020-01-30-heartbeats.html +++ /dev/null @@ -1,39 +0,0 @@ ---- -layout: post -title: "I believe every human has a finite number of heartbeats. I don't intend to waste any of mine." -date: 2020-01-30 23:45:13 -0400 -background: '/img/posts/05.jpg' ---- - -Never in all their history have men been able truly to conceive of the world as one: a single sphere, a globe, having the qualities of a globe, a round earth in which all the directions eventually meet, in which there is no center because every point, or none, is center — an equal earth which all men occupy as equals. The airman's earth, if free men make it, will be truly round: a globe in practice, not in theory.
- -Science cuts two ways, of course; its products can be used for both good and evil. But there's no turning back from science. The early warnings about technological dangers also come from science.
- -What was most significant about the lunar voyage was not that man set foot on the Moon but that they set eye on the earth.
- -A Chinese tale tells of some men sent to harm a young girl who, upon seeing her beauty, become her protectors rather than her violators. That's how I felt seeing the Earth for the first time. I could not help but love and cherish her.
- -For those who have seen the Earth from space, and for the hundreds and perhaps thousands more who will, the experience most certainly changes your perspective. The things that we share in our world are far more valuable than those which divide us.
- -There can be no thought of finishing for ‘aiming for the stars.’ Both figuratively and literally, it is a task to occupy the generations. And no matter how much progress one makes, there is always the thrill of just beginning.
- -There can be no thought of finishing for ‘aiming for the stars.’ Both figuratively and literally, it is a task to occupy the generations. And no matter how much progress one makes, there is always the thrill of just beginning.
- -The dreams of yesterday are the hopes of today and the reality of tomorrow. Science has not yet mastered prophecy. We predict too much for the next year and yet far too little for the next ten.- -
Spaceflights cannot be stopped. This is not the work of any one man or even a group of men. It is a historical process which mankind is carrying out in accordance with the natural laws of human development.
- -As we got further and further away, it [the Earth] diminished in size. Finally it shrank to the size of a marble, the most beautiful you can imagine. That beautiful, warm, living object looked so fragile, so delicate, that if you touched it with a finger it would crumble and fall apart. Seeing this has to change a man.
- -Space, the final frontier. These are the voyages of the Starship Enterprise. Its five-year mission: to explore strange new worlds, to seek out new life and new civilizations, to boldly go where no man has gone before.
- -As I stand out here in the wonders of the unknown at Hadley, I sort of realize there’s a fundamental truth to our nature, Man must explore, and this is exploration at its greatest.
- -Placeholder text by Space Ipsum. Photographs by Unsplash.
diff --git a/_posts/2020-01-31-man-must-explore.html b/_posts/2020-01-31-man-must-explore.html deleted file mode 100644 index 4dd948b4d7..0000000000 --- a/_posts/2020-01-31-man-must-explore.html +++ /dev/null @@ -1,40 +0,0 @@ ---- -layout: post -title: "Man must explore, and this is exploration at its greatest" -subtitle: "Problems look mighty small from 150 miles up" -date: 2020-01-31 10:45:13 -0400 -background: '/img/posts/06.jpg' ---- - -Never in all their history have men been able truly to conceive of the world as one: a single sphere, a globe, having the qualities of a globe, a round earth in which all the directions eventually meet, in which there is no center because every point, or none, is center — an equal earth which all men occupy as equals. The airman's earth, if free men make it, will be truly round: a globe in practice, not in theory.
- -Science cuts two ways, of course; its products can be used for both good and evil. But there's no turning back from science. The early warnings about technological dangers also come from science.
- -What was most significant about the lunar voyage was not that man set foot on the Moon but that they set eye on the earth.
- -A Chinese tale tells of some men sent to harm a young girl who, upon seeing her beauty, become her protectors rather than her violators. That's how I felt seeing the Earth for the first time. I could not help but love and cherish her.
- -For those who have seen the Earth from space, and for the hundreds and perhaps thousands more who will, the experience most certainly changes your perspective. The things that we share in our world are far more valuable than those which divide us.
- -There can be no thought of finishing for ‘aiming for the stars.’ Both figuratively and literally, it is a task to occupy the generations. And no matter how much progress one makes, there is always the thrill of just beginning.
- -There can be no thought of finishing for ‘aiming for the stars.’ Both figuratively and literally, it is a task to occupy the generations. And no matter how much progress one makes, there is always the thrill of just beginning.
- -The dreams of yesterday are the hopes of today and the reality of tomorrow. Science has not yet mastered prophecy. We predict too much for the next year and yet far too little for the next ten.- -
Spaceflights cannot be stopped. This is not the work of any one man or even a group of men. It is a historical process which mankind is carrying out in accordance with the natural laws of human development.
- -As we got further and further away, it [the Earth] diminished in size. Finally it shrank to the size of a marble, the most beautiful you can imagine. That beautiful, warm, living object looked so fragile, so delicate, that if you touched it with a finger it would crumble and fall apart. Seeing this has to change a man.
- -Space, the final frontier. These are the voyages of the Starship Enterprise. Its five-year mission: to explore strange new worlds, to seek out new life and new civilizations, to boldly go where no man has gone before.
- -As I stand out here in the wonders of the unknown at Hadley, I sort of realize there’s a fundamental truth to our nature, Man must explore, and this is exploration at its greatest.
- -Placeholder text by Space Ipsum. Photographs by Unsplash.
diff --git a/_posts/2022-10-30-Discussion-on-Generative-Modelling.md b/_posts/2022-10-30-Discussion-on-Generative-Modelling.md new file mode 100644 index 0000000000..bd7f44dfd1 --- /dev/null +++ b/_posts/2022-10-30-Discussion-on-Generative-Modelling.md @@ -0,0 +1,87 @@ +--- +layout: post +title: "Discussion Generative Modelling" +date: 2022-09-30 23:45:13 -0400 +background: '/img/posts/05.jpg' +--- + + +A generative model simulates how the data is generated +in the real world. “Modeling” is understood in almost every science as +unveiling this generating process by hypothesizing theories and testing +these theories through observations. For instance, when meteorologists +model the weather they use highly complex partial differential equations +to express the underlying physics of the weather. Or when an astronomer +models the formation of galaxies s/he encodes in his/her equations of +motion the physical laws under which stellar bodies interact. The same +is true for biologists, chemists, economists and so on. Modeling in the +sciences is in fact almost always generative modeling. + + +Let’s use x as the vector representing the set of all observed variables +whose joint distribution we would like to model. Note that for notational +simplicity and to avoid clutter, we use lower case bold (e.g. x) to denote +the underlying set of observed random variables, i.e. flattened and +concatenated such that the set is represented as a single vector. See +section A.1 for more on notation. +We assume the observed variable x is a random sample from an +unknown underlying process, whose true (probability) distribution p∗(x) +is unknown. We attempt to approximate this underlying process with a +chosen model pθ(x), with parameters θ: +x ∼ pθ(x) (1.1) +Learning is, most commonly, the process of searching for a value of +the parameters θ such that the probability distribution function given +by the model, pθ(x), approximates the true distribution of the data, +denoted by p∗(x), such that for any observed x: +pθ(x) ≈ p∗(x) (1.2) +Naturally, we wish pθ(x) to be sufficiently flexible to be able to +adapt to the data, such that we have a chance of obtaining a sufficiently +accurate model. At the same time, we wish to be able to incorporate + + + + +Continuing with the relay race example from section 16.1, suppose we nameAlice’s finishing timet0, Bob’s finishing timet1, and Carol’s finishing timet2. +As we saw earlier, our estimate oft1depends ont0. Our estimate oft2dependsdirectly ont1but only indirectly ont0. +We can draw this relationship in a directedgraphical model, illustrated in figure 16.2. +Formally, a directed graphical model defined on variablesxis defined by adirected acyclic graphGwhose vertices are the random variables in the model, anda set oflocal conditional probability distributions p(xi| P aG(xi)), whereP aG(xi) gives the parents ofxiinG. +The probability distribution overxis givenbyp(x) = Πip(xi| PaG(xi)). (16.1) +In our relay race example, this means that, using the graph drawn in figure 16.2,p(t0, t1, t2) = p(t0)p(t1| t0)p(t2| t1). (16.2) +This is our first time seeing a structured probabilistic model in action. Wecan examine the cost of using it, to observe how structured modeling has manyadvantages relative to unstructured modeling. + + + +
+
+
+## Parameterizing Conditional Distributions with Neural Networks
+In case of neuralnetwork based image classification LeCun et al., 1998, for example,neural networks parameterize a categorical distribution pθ(y|x) over a
+class label y, conditioned on an image x.
+
+p = NeuralNet(x) (1.4)
+
+pθ(y|x) = Categorical(y; p) (1.5)
+
+where the last operation of NeuralNet(.) is typically a softmax() function
+such that summation pi = 1.
+
+
+The joint
+distribution over the variables of such models factorizes as a product of prior and conditional distributions:
+
+
+
+
+A more flexible way to parameterize such conditional distributions is with neural networks. In this case,
+neural networks take as input the parents of a variable in a directed graph, and produce the distributional parameters η over that variable:
+
+η = NeuralNet(P a(x)) (1.7)
+
+pθ(x/P a(x)) = pθ(x/η) (1.8)
+
+We will now discuss how to learn the parameters of such models, if all the variables are observed in the data.
+
+
+## V.I. Learn to see steps of modelling as probability definitions
+
+
diff --git a/_posts/2023-08-01-Inspiring-Quotes.md b/_posts/2023-08-01-Inspiring-Quotes.md
new file mode 100644
index 0000000000..59924f551c
--- /dev/null
+++ b/_posts/2023-08-01-Inspiring-Quotes.md
@@ -0,0 +1,18 @@
+---
+layout: post
+title: "Inspiring-Quotes"
+date: 2022-09-30 23:45:13 -0400
+background: '/img/posts/05.jpg'
+---
+
+
+"Intelligence is the ability to adapt to change " subtitle: "Stephen Hawking"
+
+"Chance gives rise to thoughts, +and chance removes them. + -Pascal (1670)"
+ + +"The essence of the present theory is that no probability, direct, prior, or +posterior, is simply a frequency. + -H. Jeffreys (1939)"
diff --git a/_posts/2023-08-12-My-AI-Journey-through-Books.md b/_posts/2023-08-12-My-AI-Journey-through-Books.md new file mode 100644 index 0000000000..317c74ed6f --- /dev/null +++ b/_posts/2023-08-12-My-AI-Journey-through-Books.md @@ -0,0 +1,25 @@ +--- +layout: post +title: "My AI Journey through Books" +date: 2022-08-12 13:45:13 -0400 +background: '/img/posts/05.jpg' +--- + +## Books read by me +1.[_Probability Theory:The Logic of Science_](https://www.google.co.in/books/edition/Probability_Theory/UjsgAwAAQBAJ?hl=en&gbpv=0) -by E.T. Jaynes + +2.[_The Master Algorithm_](https://www.amazon.in/Master-Algorithm-Ultimate-Learning-Machine/dp/0465065708) -by Pedro Domingos + +3.[_How to Create a Mind_](https://www.amazon.in/How-Create-Mind-Thought-Revealed/dp/0143124048) -by Ray Kurzweil + +4.[_Deep Learning_](https://www.deeplearningbook.org/) -by Ian Goodfellow and Yoshua Bengio and Aaron Courville + +5.[_Pattern Recognition and Machine Learning_](https://www.microsoft.com/en-us/research/uploads/prod/2006/01/Bishop-Pattern-Recognition-and-Machine-Learning-2006.pdf) - by Christopher Bishop + +6.[_The Theory of Probability_](https://www.google.co.in/books/edition/The_Theory_of_Probability/vh9Act9rtzQC?hl=en&gbpv=0) -by Sir Harold Jeffreys · 1998 + +7.[_Random Number Generation and Monte Carlo Methods_](https://www.google.co.in/books/edition/Random_Number_Generation_and_Monte_Carlo/MYnqBwAAQBAJ?hl=en&gbpv=0) -By James E. Gentle · 2006 + +8.[_Bayesian Brain: Probabilistic Approaches to Neural Coding_](https://direct.mit.edu/books/edited-volume/2884/Bayesian-BrainProbabilistic-Approaches-to-Neural) -Edited by Kenji Doya, Shin Ishii, Alexandre Pouget, Rajesh P.N. Rao + +9.[_Bayesian Models of Perception and Action_](https://mitpress.mit.edu/9780262047593/bayesian-models-of-perception-and-action/#:~:text=Many%20forms%20of%20perception%20and,with%20noisy%20and%20ambiguous%20data.) -By Wei Ji Ma, Konrad Paul Kording and Daniel Goldreich diff --git a/about.html b/about.html index c661ef64e4..21b8877968 100644 --- a/about.html +++ b/about.html @@ -1,12 +1,39 @@ ---- -layout: page -title: About Me -description: This is what I do. -background: '/img/bg-about.jpg' ---- + + + +A Learning theory enthusiast. Currently working on applying Deep generative + Learning to improve clinical research algorithms. + Energetic Senior Software Engineer with 4+ years of experience + having a diverse skill-set and creative drive to software application + development. Proficient in writing code in various languages. + And having extensive experience in developing customized Deep + learning/Machine Learning architectures from scratch with good + mathematical understanding.
+ +My goal is simple "A complete understanding of how the brain works". + This passion led me to explore many fields like Learning theory, Bayesian inspired algorithms,Reinforcement Learning,Causal Inference,psychology,psychoanalysis,Neuroscience,Spoken language Understanding and EEG recording Analysis.
-Lorem ipsum dolor sit amet, consectetur adipisicing elit. Sed quisquam ut perspiciatis, repudiandae nulla animi iste vel, praesentium repellendus molestias aliquid consequatur, earum rem qui error voluptates eius enim consequuntur!
+Currently I'm open to PhD/Research position
-Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ex alias, earum consectetur quia natus ducimus voluptate explicabo, hic porro reprehenderit, quasi? Tenetur ipsum distinctio laboriosam perspiciatis officiis dolore, architecto id.
+ My Resume -Lorem ipsum dolor sit amet, consectetur adipisicing elit. Totam inventore aspernatur repellendus incidunt adipisci modi voluptates recusandae iste eligendi, repudiandae corporis quod aut, optio! Explicabo quaerat unde voluptatem! Itaque, eum!
+ + + Github Link + + Linkedin Link + +Feel free to contact me:
+email: satish.pas@gmail.com
+phone: +918971000639
+ + diff --git a/contact.html b/contact.html deleted file mode 100644 index e23d22616e..0000000000 --- a/contact.html +++ /dev/null @@ -1,44 +0,0 @@ ---- -layout: page -title: Contact Me -description: Have questions? I have answers. -background: '/img/bg-contact.jpg' -form: true ---- - -Want to get in touch? Fill out the form below to send me a message and I will get back to you as soon as possible!
- diff --git a/img/directed-model-exp.png b/img/directed-model-exp.png new file mode 100644 index 0000000000..eab21ad675 Binary files /dev/null and b/img/directed-model-exp.png differ diff --git a/img/directed-model-formula.png b/img/directed-model-formula.png new file mode 100644 index 0000000000..16bc10e3ba Binary files /dev/null and b/img/directed-model-formula.png differ diff --git a/index copy.html b/index copy.html new file mode 100644 index 0000000000..9fcc79a236 --- /dev/null +++ b/index copy.html @@ -0,0 +1,4 @@ +--- +layout: home +background: '/img/bg-index.jpg' +--- diff --git a/index.html b/index.html index 9fcc79a236..7aea705dba 100644 --- a/index.html +++ b/index.html @@ -1,4 +1,16 @@ ---- -layout: home -background: '/img/bg-index.jpg' ---- + + + + + + + +true if the Template objects are equal. This method
+ # does NOT normalize either Template before doing the comparison.
+ #
+ # @param [Object] template The Template to compare.
+ #
+ # @return [TrueClass, FalseClass]
+ # true if the Templates are equivalent, false
+ # otherwise.
+ def ==(template)
+ return false unless template.kind_of?(Template)
+ return self.pattern == template.pattern
+ end
+
+ ##
+ # Addressable::Template makes no distinction between `==` and `eql?`.
+ #
+ # @see #==
+ alias_method :eql?, :==
+
+ ##
+ # Extracts a mapping from the URI using a URI Template pattern.
+ #
+ # @param [Addressable::URI, #to_str] uri
+ # The URI to extract from.
+ #
+ # @param [#restore, #match] processor
+ # A template processor object may optionally be supplied.
+ #
+ # The object should respond to either the restore or
+ # match messages or both. The restore method should
+ # take two parameters: `[String] name` and `[String] value`.
+ # The restore method should reverse any transformations that
+ # have been performed on the value to ensure a valid URI.
+ # The match method should take a single
+ # parameter: `[String] name`. The match method should return
+ # a String containing a regular expression capture group for
+ # matching on that particular variable. The default value is `".*?"`.
+ # The match method has no effect on multivariate operator
+ # expansions.
+ #
+ # @return [Hash, NilClass]
+ # The Hash mapping that was extracted from the URI, or
+ # nil if the URI didn't match the template.
+ #
+ # @example
+ # class ExampleProcessor
+ # def self.restore(name, value)
+ # return value.gsub(/\+/, " ") if name == "query"
+ # return value
+ # end
+ #
+ # def self.match(name)
+ # return ".*?" if name == "first"
+ # return ".*"
+ # end
+ # end
+ #
+ # uri = Addressable::URI.parse(
+ # "http://example.com/search/an+example+search+query/"
+ # )
+ # Addressable::Template.new(
+ # "http://example.com/search/{query}/"
+ # ).extract(uri, ExampleProcessor)
+ # #=> {"query" => "an example search query"}
+ #
+ # uri = Addressable::URI.parse("http://example.com/a/b/c/")
+ # Addressable::Template.new(
+ # "http://example.com/{first}/{second}/"
+ # ).extract(uri, ExampleProcessor)
+ # #=> {"first" => "a", "second" => "b/c"}
+ #
+ # uri = Addressable::URI.parse("http://example.com/a/b/c/")
+ # Addressable::Template.new(
+ # "http://example.com/{first}/{-list|/|second}/"
+ # ).extract(uri)
+ # #=> {"first" => "a", "second" => ["b", "c"]}
+ def extract(uri, processor=nil)
+ match_data = self.match(uri, processor)
+ return (match_data ? match_data.mapping : nil)
+ end
+
+ ##
+ # Extracts match data from the URI using a URI Template pattern.
+ #
+ # @param [Addressable::URI, #to_str] uri
+ # The URI to extract from.
+ #
+ # @param [#restore, #match] processor
+ # A template processor object may optionally be supplied.
+ #
+ # The object should respond to either the restore or
+ # match messages or both. The restore method should
+ # take two parameters: `[String] name` and `[String] value`.
+ # The restore method should reverse any transformations that
+ # have been performed on the value to ensure a valid URI.
+ # The match method should take a single
+ # parameter: `[String] name`. The match method should return
+ # a String containing a regular expression capture group for
+ # matching on that particular variable. The default value is `".*?"`.
+ # The match method has no effect on multivariate operator
+ # expansions.
+ #
+ # @return [Hash, NilClass]
+ # The Hash mapping that was extracted from the URI, or
+ # nil if the URI didn't match the template.
+ #
+ # @example
+ # class ExampleProcessor
+ # def self.restore(name, value)
+ # return value.gsub(/\+/, " ") if name == "query"
+ # return value
+ # end
+ #
+ # def self.match(name)
+ # return ".*?" if name == "first"
+ # return ".*"
+ # end
+ # end
+ #
+ # uri = Addressable::URI.parse(
+ # "http://example.com/search/an+example+search+query/"
+ # )
+ # match = Addressable::Template.new(
+ # "http://example.com/search/{query}/"
+ # ).match(uri, ExampleProcessor)
+ # match.variables
+ # #=> ["query"]
+ # match.captures
+ # #=> ["an example search query"]
+ #
+ # uri = Addressable::URI.parse("http://example.com/a/b/c/")
+ # match = Addressable::Template.new(
+ # "http://example.com/{first}/{+second}/"
+ # ).match(uri, ExampleProcessor)
+ # match.variables
+ # #=> ["first", "second"]
+ # match.captures
+ # #=> ["a", "b/c"]
+ #
+ # uri = Addressable::URI.parse("http://example.com/a/b/c/")
+ # match = Addressable::Template.new(
+ # "http://example.com/{first}{/second*}/"
+ # ).match(uri)
+ # match.variables
+ # #=> ["first", "second"]
+ # match.captures
+ # #=> ["a", ["b", "c"]]
+ def match(uri, processor=nil)
+ uri = Addressable::URI.parse(uri) unless uri.is_a?(Addressable::URI)
+ mapping = {}
+
+ # First, we need to process the pattern, and extract the values.
+ expansions, expansion_regexp =
+ parse_template_pattern(pattern, processor)
+
+ return nil unless uri.to_str.match(expansion_regexp)
+ unparsed_values = uri.to_str.scan(expansion_regexp).flatten
+
+ if uri.to_str == pattern
+ return Addressable::Template::MatchData.new(uri, self, mapping)
+ elsif expansions.size > 0
+ index = 0
+ expansions.each do |expansion|
+ _, operator, varlist = *expansion.match(EXPRESSION)
+ varlist.split(',').each do |varspec|
+ _, name, modifier = *varspec.match(VARSPEC)
+ mapping[name] ||= nil
+ case operator
+ when nil, '+', '#', '/', '.'
+ unparsed_value = unparsed_values[index]
+ name = varspec[VARSPEC, 1]
+ value = unparsed_value
+ value = value.split(JOINERS[operator]) if value && modifier == '*'
+ when ';', '?', '&'
+ if modifier == '*'
+ if unparsed_values[index]
+ value = unparsed_values[index].split(JOINERS[operator])
+ value = value.inject({}) do |acc, v|
+ key, val = v.split('=')
+ val = "" if val.nil?
+ acc[key] = val
+ acc
+ end
+ end
+ else
+ if (unparsed_values[index])
+ name, value = unparsed_values[index].split('=')
+ value = "" if value.nil?
+ end
+ end
+ end
+ if processor != nil && processor.respond_to?(:restore)
+ value = processor.restore(name, value)
+ end
+ if processor == nil
+ if value.is_a?(Hash)
+ value = value.inject({}){|acc, (k, v)|
+ acc[Addressable::URI.unencode_component(k)] =
+ Addressable::URI.unencode_component(v)
+ acc
+ }
+ elsif value.is_a?(Array)
+ value = value.map{|v| Addressable::URI.unencode_component(v) }
+ else
+ value = Addressable::URI.unencode_component(value)
+ end
+ end
+ if !mapping.has_key?(name) || mapping[name].nil?
+ # Doesn't exist, set to value (even if value is nil)
+ mapping[name] = value
+ end
+ index = index + 1
+ end
+ end
+ return Addressable::Template::MatchData.new(uri, self, mapping)
+ else
+ return nil
+ end
+ end
+
+ ##
+ # Expands a URI template into another URI template.
+ #
+ # @param [Hash] mapping The mapping that corresponds to the pattern.
+ # @param [#validate, #transform] processor
+ # An optional processor object may be supplied.
+ # @param [Boolean] normalize_values
+ # Optional flag to enable/disable unicode normalization. Default: true
+ #
+ # The object should respond to either the validate or
+ # transform messages or both. Both the validate and
+ # transform methods should take two parameters: name and
+ # value. The validate method should return true
+ # or false; true if the value of the variable is valid,
+ # false otherwise. An InvalidTemplateValueError
+ # exception will be raised if the value is invalid. The transform
+ # method should return the transformed variable value as a String.
+ # If a transform method is used, the value will not be percent
+ # encoded automatically. Unicode normalization will be performed both
+ # before and after sending the value to the transform method.
+ #
+ # @return [Addressable::Template] The partially expanded URI template.
+ #
+ # @example
+ # Addressable::Template.new(
+ # "http://example.com/{one}/{two}/"
+ # ).partial_expand({"one" => "1"}).pattern
+ # #=> "http://example.com/1/{two}/"
+ #
+ # Addressable::Template.new(
+ # "http://example.com/{?one,two}/"
+ # ).partial_expand({"one" => "1"}).pattern
+ # #=> "http://example.com/?one=1{&two}/"
+ #
+ # Addressable::Template.new(
+ # "http://example.com/{?one,two,three}/"
+ # ).partial_expand({"one" => "1", "three" => 3}).pattern
+ # #=> "http://example.com/?one=1{&two}&three=3"
+ def partial_expand(mapping, processor=nil, normalize_values=true)
+ result = self.pattern.dup
+ mapping = normalize_keys(mapping)
+ result.gsub!( EXPRESSION ) do |capture|
+ transform_partial_capture(mapping, capture, processor, normalize_values)
+ end
+ return Addressable::Template.new(result)
+ end
+
+ ##
+ # Expands a URI template into a full URI.
+ #
+ # @param [Hash] mapping The mapping that corresponds to the pattern.
+ # @param [#validate, #transform] processor
+ # An optional processor object may be supplied.
+ # @param [Boolean] normalize_values
+ # Optional flag to enable/disable unicode normalization. Default: true
+ #
+ # The object should respond to either the validate or
+ # transform messages or both. Both the validate and
+ # transform methods should take two parameters: name and
+ # value. The validate method should return true
+ # or false; true if the value of the variable is valid,
+ # false otherwise. An InvalidTemplateValueError
+ # exception will be raised if the value is invalid. The transform
+ # method should return the transformed variable value as a String.
+ # If a transform method is used, the value will not be percent
+ # encoded automatically. Unicode normalization will be performed both
+ # before and after sending the value to the transform method.
+ #
+ # @return [Addressable::URI] The expanded URI template.
+ #
+ # @example
+ # class ExampleProcessor
+ # def self.validate(name, value)
+ # return !!(value =~ /^[\w ]+$/) if name == "query"
+ # return true
+ # end
+ #
+ # def self.transform(name, value)
+ # return value.gsub(/ /, "+") if name == "query"
+ # return value
+ # end
+ # end
+ #
+ # Addressable::Template.new(
+ # "http://example.com/search/{query}/"
+ # ).expand(
+ # {"query" => "an example search query"},
+ # ExampleProcessor
+ # ).to_str
+ # #=> "http://example.com/search/an+example+search+query/"
+ #
+ # Addressable::Template.new(
+ # "http://example.com/search/{query}/"
+ # ).expand(
+ # {"query" => "an example search query"}
+ # ).to_str
+ # #=> "http://example.com/search/an%20example%20search%20query/"
+ #
+ # Addressable::Template.new(
+ # "http://example.com/search/{query}/"
+ # ).expand(
+ # {"query" => "bogus!"},
+ # ExampleProcessor
+ # ).to_str
+ # #=> Addressable::Template::InvalidTemplateValueError
+ def expand(mapping, processor=nil, normalize_values=true)
+ result = self.pattern.dup
+ mapping = normalize_keys(mapping)
+ result.gsub!( EXPRESSION ) do |capture|
+ transform_capture(mapping, capture, processor, normalize_values)
+ end
+ return Addressable::URI.parse(result)
+ end
+
+ ##
+ # Returns an Array of variables used within the template pattern.
+ # The variables are listed in the Array in the order they appear within
+ # the pattern. Multiple occurrences of a variable within a pattern are
+ # not represented in this Array.
+ #
+ # @return [Array] The variables present in the template's pattern.
+ def variables
+ @variables ||= ordered_variable_defaults.map { |var, val| var }.uniq
+ end
+ alias_method :keys, :variables
+ alias_method :names, :variables
+
+ ##
+ # Returns a mapping of variables to their default values specified
+ # in the template. Variables without defaults are not returned.
+ #
+ # @return [Hash] Mapping of template variables to their defaults
+ def variable_defaults
+ @variable_defaults ||=
+ Hash[*ordered_variable_defaults.reject { |k, v| v.nil? }.flatten]
+ end
+
+ ##
+ # Coerces a template into a `Regexp` object. This regular expression will
+ # behave very similarly to the actual template, and should match the same
+ # URI values, but it cannot fully handle, for example, values that would
+ # extract to an `Array`.
+ #
+ # @return [Regexp] A regular expression which should match the template.
+ def to_regexp
+ _, source = parse_template_pattern(pattern)
+ Regexp.new(source)
+ end
+
+ ##
+ # Returns the source of the coerced `Regexp`.
+ #
+ # @return [String] The source of the `Regexp` given by {#to_regexp}.
+ #
+ # @api private
+ def source
+ self.to_regexp.source
+ end
+
+ ##
+ # Returns the named captures of the coerced `Regexp`.
+ #
+ # @return [Hash] The named captures of the `Regexp` given by {#to_regexp}.
+ #
+ # @api private
+ def named_captures
+ self.to_regexp.named_captures
+ end
+
+ private
+ def ordered_variable_defaults
+ @ordered_variable_defaults ||= begin
+ expansions, _ = parse_template_pattern(pattern)
+ expansions.flat_map do |capture|
+ _, _, varlist = *capture.match(EXPRESSION)
+ varlist.split(',').map do |varspec|
+ varspec[VARSPEC, 1]
+ end
+ end
+ end
+ end
+
+
+ ##
+ # Loops through each capture and expands any values available in mapping
+ #
+ # @param [Hash] mapping
+ # Set of keys to expand
+ # @param [String] capture
+ # The expression to expand
+ # @param [#validate, #transform] processor
+ # An optional processor object may be supplied.
+ # @param [Boolean] normalize_values
+ # Optional flag to enable/disable unicode normalization. Default: true
+ #
+ # The object should respond to either the validate or
+ # transform messages or both. Both the validate and
+ # transform methods should take two parameters: name and
+ # value. The validate method should return true
+ # or false; true if the value of the variable is valid,
+ # false otherwise. An InvalidTemplateValueError exception
+ # will be raised if the value is invalid. The transform method
+ # should return the transformed variable value as a String. If a
+ # transform method is used, the value will not be percent encoded
+ # automatically. Unicode normalization will be performed both before and
+ # after sending the value to the transform method.
+ #
+ # @return [String] The expanded expression
+ def transform_partial_capture(mapping, capture, processor = nil,
+ normalize_values = true)
+ _, operator, varlist = *capture.match(EXPRESSION)
+
+ vars = varlist.split(",")
+
+ if operator == "?"
+ # partial expansion of form style query variables sometimes requires a
+ # slight reordering of the variables to produce a valid url.
+ first_to_expand = vars.find { |varspec|
+ _, name, _ = *varspec.match(VARSPEC)
+ mapping.key?(name) && !mapping[name].nil?
+ }
+
+ vars = [first_to_expand] + vars.reject {|varspec| varspec == first_to_expand} if first_to_expand
+ end
+
+ vars.
+ inject("".dup) do |acc, varspec|
+ _, name, _ = *varspec.match(VARSPEC)
+ next_val = if mapping.key? name
+ transform_capture(mapping, "{#{operator}#{varspec}}",
+ processor, normalize_values)
+ else
+ "{#{operator}#{varspec}}"
+ end
+ # If we've already expanded at least one '?' operator with non-empty
+ # value, change to '&'
+ operator = "&" if (operator == "?") && (next_val != "")
+ acc << next_val
+ end
+ end
+
+ ##
+ # Transforms a mapped value so that values can be substituted into the
+ # template.
+ #
+ # @param [Hash] mapping The mapping to replace captures
+ # @param [String] capture
+ # The expression to replace
+ # @param [#validate, #transform] processor
+ # An optional processor object may be supplied.
+ # @param [Boolean] normalize_values
+ # Optional flag to enable/disable unicode normalization. Default: true
+ #
+ #
+ # The object should respond to either the validate or
+ # transform messages or both. Both the validate and
+ # transform methods should take two parameters: name and
+ # value. The validate method should return true
+ # or false; true if the value of the variable is valid,
+ # false otherwise. An InvalidTemplateValueError exception
+ # will be raised if the value is invalid. The transform method
+ # should return the transformed variable value as a String. If a
+ # transform method is used, the value will not be percent encoded
+ # automatically. Unicode normalization will be performed both before and
+ # after sending the value to the transform method.
+ #
+ # @return [String] The expanded expression
+ def transform_capture(mapping, capture, processor=nil,
+ normalize_values=true)
+ _, operator, varlist = *capture.match(EXPRESSION)
+ return_value = varlist.split(',').inject([]) do |acc, varspec|
+ _, name, modifier = *varspec.match(VARSPEC)
+ value = mapping[name]
+ unless value == nil || value == {}
+ allow_reserved = %w(+ #).include?(operator)
+ # Common primitives where the .to_s output is well-defined
+ if Numeric === value || Symbol === value ||
+ value == true || value == false
+ value = value.to_s
+ end
+ length = modifier.gsub(':', '').to_i if modifier =~ /^:\d+/
+
+ unless (Hash === value) ||
+ value.respond_to?(:to_ary) || value.respond_to?(:to_str)
+ raise TypeError,
+ "Can't convert #{value.class} into String or Array."
+ end
+
+ value = normalize_value(value) if normalize_values
+
+ if processor == nil || !processor.respond_to?(:transform)
+ # Handle percent escaping
+ if allow_reserved
+ encode_map =
+ Addressable::URI::CharacterClasses::RESERVED +
+ Addressable::URI::CharacterClasses::UNRESERVED
+ else
+ encode_map = Addressable::URI::CharacterClasses::UNRESERVED
+ end
+ if value.kind_of?(Array)
+ transformed_value = value.map do |val|
+ if length
+ Addressable::URI.encode_component(val[0...length], encode_map)
+ else
+ Addressable::URI.encode_component(val, encode_map)
+ end
+ end
+ unless modifier == "*"
+ transformed_value = transformed_value.join(',')
+ end
+ elsif value.kind_of?(Hash)
+ transformed_value = value.map do |key, val|
+ if modifier == "*"
+ "#{
+ Addressable::URI.encode_component( key, encode_map)
+ }=#{
+ Addressable::URI.encode_component( val, encode_map)
+ }"
+ else
+ "#{
+ Addressable::URI.encode_component( key, encode_map)
+ },#{
+ Addressable::URI.encode_component( val, encode_map)
+ }"
+ end
+ end
+ unless modifier == "*"
+ transformed_value = transformed_value.join(',')
+ end
+ else
+ if length
+ transformed_value = Addressable::URI.encode_component(
+ value[0...length], encode_map)
+ else
+ transformed_value = Addressable::URI.encode_component(
+ value, encode_map)
+ end
+ end
+ end
+
+ # Process, if we've got a processor
+ if processor != nil
+ if processor.respond_to?(:validate)
+ if !processor.validate(name, value)
+ display_value = value.kind_of?(Array) ? value.inspect : value
+ raise InvalidTemplateValueError,
+ "#{name}=#{display_value} is an invalid template value."
+ end
+ end
+ if processor.respond_to?(:transform)
+ transformed_value = processor.transform(name, value)
+ if normalize_values
+ transformed_value = normalize_value(transformed_value)
+ end
+ end
+ end
+ acc << [name, transformed_value]
+ end
+ acc
+ end
+ return "" if return_value.empty?
+ join_values(operator, return_value)
+ end
+
+ ##
+ # Takes a set of values, and joins them together based on the
+ # operator.
+ #
+ # @param [String, Nil] operator One of the operators from the set
+ # (?,&,+,#,;,/,.), or nil if there wasn't one.
+ # @param [Array] return_value
+ # The set of return values (as [variable_name, value] tuples) that will
+ # be joined together.
+ #
+ # @return [String] The transformed mapped value
+ def join_values(operator, return_value)
+ leader = LEADERS.fetch(operator, '')
+ joiner = JOINERS.fetch(operator, ',')
+ case operator
+ when '&', '?'
+ leader + return_value.map{|k,v|
+ if v.is_a?(Array) && v.first =~ /=/
+ v.join(joiner)
+ elsif v.is_a?(Array)
+ v.map{|inner_value| "#{k}=#{inner_value}"}.join(joiner)
+ else
+ "#{k}=#{v}"
+ end
+ }.join(joiner)
+ when ';'
+ return_value.map{|k,v|
+ if v.is_a?(Array) && v.first =~ /=/
+ ';' + v.join(";")
+ elsif v.is_a?(Array)
+ ';' + v.map{|inner_value| "#{k}=#{inner_value}"}.join(";")
+ else
+ v && v != '' ? ";#{k}=#{v}" : ";#{k}"
+ end
+ }.join
+ else
+ leader + return_value.map{|k,v| v}.join(joiner)
+ end
+ end
+
+ ##
+ # Takes a set of values, and joins them together based on the
+ # operator.
+ #
+ # @param [Hash, Array, String] value
+ # Normalizes keys and values with IDNA#unicode_normalize_kc
+ #
+ # @return [Hash, Array, String] The normalized values
+ def normalize_value(value)
+ unless value.is_a?(Hash)
+ value = value.respond_to?(:to_ary) ? value.to_ary : value.to_str
+ end
+
+ # Handle unicode normalization
+ if value.kind_of?(Array)
+ value.map! { |val| Addressable::IDNA.unicode_normalize_kc(val) }
+ elsif value.kind_of?(Hash)
+ value = value.inject({}) { |acc, (k, v)|
+ acc[Addressable::IDNA.unicode_normalize_kc(k)] =
+ Addressable::IDNA.unicode_normalize_kc(v)
+ acc
+ }
+ else
+ value = Addressable::IDNA.unicode_normalize_kc(value)
+ end
+ value
+ end
+
+ ##
+ # Generates a hash with string keys
+ #
+ # @param [Hash] mapping A mapping hash to normalize
+ #
+ # @return [Hash]
+ # A hash with stringified keys
+ def normalize_keys(mapping)
+ return mapping.inject({}) do |accu, pair|
+ name, value = pair
+ if Symbol === name
+ name = name.to_s
+ elsif name.respond_to?(:to_str)
+ name = name.to_str
+ else
+ raise TypeError,
+ "Can't convert #{name.class} into String."
+ end
+ accu[name] = value
+ accu
+ end
+ end
+
+ ##
+ # Generates the Regexp that parses a template pattern. Memoizes the
+ # value if template processor not set (processors may not be deterministic)
+ #
+ # @param [String] pattern The URI template pattern.
+ # @param [#match] processor The template processor to use.
+ #
+ # @return [Array, Regexp]
+ # An array of expansion variables nad a regular expression which may be
+ # used to parse a template pattern
+ def parse_template_pattern(pattern, processor = nil)
+ if processor.nil? && pattern == @pattern
+ @cached_template_parse ||=
+ parse_new_template_pattern(pattern, processor)
+ else
+ parse_new_template_pattern(pattern, processor)
+ end
+ end
+
+ ##
+ # Generates the Regexp that parses a template pattern.
+ #
+ # @param [String] pattern The URI template pattern.
+ # @param [#match] processor The template processor to use.
+ #
+ # @return [Array, Regexp]
+ # An array of expansion variables nad a regular expression which may be
+ # used to parse a template pattern
+ def parse_new_template_pattern(pattern, processor = nil)
+ # Escape the pattern. The two gsubs restore the escaped curly braces
+ # back to their original form. Basically, escape everything that isn't
+ # within an expansion.
+ escaped_pattern = Regexp.escape(
+ pattern
+ ).gsub(/\\\{(.*?)\\\}/) do |escaped|
+ escaped.gsub(/\\(.)/, "\\1")
+ end
+
+ expansions = []
+
+ # Create a regular expression that captures the values of the
+ # variables in the URI.
+ regexp_string = escaped_pattern.gsub( EXPRESSION ) do |expansion|
+
+ expansions << expansion
+ _, operator, varlist = *expansion.match(EXPRESSION)
+ leader = Regexp.escape(LEADERS.fetch(operator, ''))
+ joiner = Regexp.escape(JOINERS.fetch(operator, ','))
+ combined = varlist.split(',').map do |varspec|
+ _, name, modifier = *varspec.match(VARSPEC)
+
+ result = processor && processor.respond_to?(:match) ? processor.match(name) : nil
+ if result
+ "(?<#{name}>#{ result })"
+ else
+ group = case operator
+ when '+'
+ "#{ RESERVED }*?"
+ when '#'
+ "#{ RESERVED }*?"
+ when '/'
+ "#{ UNRESERVED }*?"
+ when '.'
+ "#{ UNRESERVED.gsub('\.', '') }*?"
+ when ';'
+ "#{ UNRESERVED }*=?#{ UNRESERVED }*?"
+ when '?'
+ "#{ UNRESERVED }*=#{ UNRESERVED }*?"
+ when '&'
+ "#{ UNRESERVED }*=#{ UNRESERVED }*?"
+ else
+ "#{ UNRESERVED }*?"
+ end
+ if modifier == '*'
+ "(?<#{name}>#{group}(?:#{joiner}?#{group})*)?"
+ else
+ "(?<#{name}>#{group})?"
+ end
+ end
+ end.join("#{joiner}?")
+ "(?:|#{leader}#{combined})"
+ end
+
+ # Ensure that the regular expression matches the whole URI.
+ regexp_string = "\\A#{regexp_string}\\z"
+ return expansions, Regexp.new(regexp_string)
+ end
+
+ end
+end
diff --git a/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/lib/addressable/uri.rb b/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/lib/addressable/uri.rb
new file mode 100644
index 0000000000..14b92530ce
--- /dev/null
+++ b/vendor/bundle/ruby/2.7.0/gems/addressable-2.8.1/lib/addressable/uri.rb
@@ -0,0 +1,2560 @@
+# frozen_string_literal: true
+
+#--
+# Copyright (C) Bob Aman
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#++
+
+
+require "addressable/version"
+require "addressable/idna"
+require "public_suffix"
+
+##
+# Addressable is a library for processing links and URIs.
+module Addressable
+ ##
+ # This is an implementation of a URI parser based on
+ # RFC 3986,
+ # RFC 3987.
+ class URI
+ ##
+ # Raised if something other than a uri is supplied.
+ class InvalidURIError < StandardError
+ end
+
+ ##
+ # Container for the character classes specified in
+ # RFC 3986.
+ #
+ # Note: Concatenated and interpolated `String`s are not affected by the
+ # `frozen_string_literal` directive and must be frozen explicitly.
+ #
+ # Interpolated `String`s *were* frozen this way before Ruby 3.0:
+ # https://bugs.ruby-lang.org/issues/17104
+ module CharacterClasses
+ ALPHA = "a-zA-Z"
+ DIGIT = "0-9"
+ GEN_DELIMS = "\\:\\/\\?\\#\\[\\]\\@"
+ SUB_DELIMS = "\\!\\$\\&\\'\\(\\)\\*\\+\\,\\;\\="
+ RESERVED = (GEN_DELIMS + SUB_DELIMS).freeze
+ UNRESERVED = (ALPHA + DIGIT + "\\-\\.\\_\\~").freeze
+ PCHAR = (UNRESERVED + SUB_DELIMS + "\\:\\@").freeze
+ SCHEME = (ALPHA + DIGIT + "\\-\\+\\.").freeze
+ HOST = (UNRESERVED + SUB_DELIMS + "\\[\\:\\]").freeze
+ AUTHORITY = (PCHAR + "\\[\\:\\]").freeze
+ PATH = (PCHAR + "\\/").freeze
+ QUERY = (PCHAR + "\\/\\?").freeze
+ FRAGMENT = (PCHAR + "\\/\\?").freeze
+ end
+
+ module NormalizeCharacterClasses
+ HOST = /[^#{CharacterClasses::HOST}]/
+ UNRESERVED = /[^#{CharacterClasses::UNRESERVED}]/
+ PCHAR = /[^#{CharacterClasses::PCHAR}]/
+ SCHEME = /[^#{CharacterClasses::SCHEME}]/
+ FRAGMENT = /[^#{CharacterClasses::FRAGMENT}]/
+ QUERY = %r{[^a-zA-Z0-9\-\.\_\~\!\$\'\(\)\*\+\,\=\:\@\/\?%]|%(?!2B|2b)}
+ end
+
+ SLASH = '/'
+ EMPTY_STR = ''
+
+ URIREGEX = /^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?$/
+
+ PORT_MAPPING = {
+ "http" => 80,
+ "https" => 443,
+ "ftp" => 21,
+ "tftp" => 69,
+ "sftp" => 22,
+ "ssh" => 22,
+ "svn+ssh" => 22,
+ "telnet" => 23,
+ "nntp" => 119,
+ "gopher" => 70,
+ "wais" => 210,
+ "ldap" => 389,
+ "prospero" => 1525
+ }.freeze
+
+ ##
+ # Returns a URI object based on the parsed string.
+ #
+ # @param [String, Addressable::URI, #to_str] uri
+ # The URI string to parse.
+ # No parsing is performed if the object is already an
+ # Addressable::URI.
+ #
+ # @return [Addressable::URI] The parsed URI.
+ def self.parse(uri)
+ # If we were given nil, return nil.
+ return nil unless uri
+ # If a URI object is passed, just return itself.
+ return uri.dup if uri.kind_of?(self)
+
+ # If a URI object of the Ruby standard library variety is passed,
+ # convert it to a string, then parse the string.
+ # We do the check this way because we don't want to accidentally
+ # cause a missing constant exception to be thrown.
+ if uri.class.name =~ /^URI\b/
+ uri = uri.to_s
+ end
+
+ # Otherwise, convert to a String
+ begin
+ uri = uri.to_str
+ rescue TypeError, NoMethodError
+ raise TypeError, "Can't convert #{uri.class} into String."
+ end if not uri.is_a? String
+
+ # This Regexp supplied as an example in RFC 3986, and it works great.
+ scan = uri.scan(URIREGEX)
+ fragments = scan[0]
+ scheme = fragments[1]
+ authority = fragments[3]
+ path = fragments[4]
+ query = fragments[6]
+ fragment = fragments[8]
+ user = nil
+ password = nil
+ host = nil
+ port = nil
+ if authority != nil
+ # The Regexp above doesn't split apart the authority.
+ userinfo = authority[/^([^\[\]]*)@/, 1]
+ if userinfo != nil
+ user = userinfo.strip[/^([^:]*):?/, 1]
+ password = userinfo.strip[/:(.*)$/, 1]
+ end
+ host = authority.sub(
+ /^([^\[\]]*)@/, EMPTY_STR
+ ).sub(
+ /:([^:@\[\]]*?)$/, EMPTY_STR
+ )
+ port = authority[/:([^:@\[\]]*?)$/, 1]
+ end
+ if port == EMPTY_STR
+ port = nil
+ end
+
+ return new(
+ :scheme => scheme,
+ :user => user,
+ :password => password,
+ :host => host,
+ :port => port,
+ :path => path,
+ :query => query,
+ :fragment => fragment
+ )
+ end
+
+ ##
+ # Converts an input to a URI. The input does not have to be a valid
+ # URI — the method will use heuristics to guess what URI was intended.
+ # This is not standards-compliant, merely user-friendly.
+ #
+ # @param [String, Addressable::URI, #to_str] uri
+ # The URI string to parse.
+ # No parsing is performed if the object is already an
+ # Addressable::URI.
+ # @param [Hash] hints
+ # A Hash of hints to the heuristic parser.
+ # Defaults to {:scheme => "http"}.
+ #
+ # @return [Addressable::URI] The parsed URI.
+ def self.heuristic_parse(uri, hints={})
+ # If we were given nil, return nil.
+ return nil unless uri
+ # If a URI object is passed, just return itself.
+ return uri.dup if uri.kind_of?(self)
+
+ # If a URI object of the Ruby standard library variety is passed,
+ # convert it to a string, then parse the string.
+ # We do the check this way because we don't want to accidentally
+ # cause a missing constant exception to be thrown.
+ if uri.class.name =~ /^URI\b/
+ uri = uri.to_s
+ end
+
+ if !uri.respond_to?(:to_str)
+ raise TypeError, "Can't convert #{uri.class} into String."
+ end
+ # Otherwise, convert to a String
+ uri = uri.to_str.dup.strip
+ hints = {
+ :scheme => "http"
+ }.merge(hints)
+ case uri
+ when /^http:\//i
+ uri.sub!(/^http:\/+/i, "http://")
+ when /^https:\//i
+ uri.sub!(/^https:\/+/i, "https://")
+ when /^feed:\/+http:\//i
+ uri.sub!(/^feed:\/+http:\/+/i, "feed:http://")
+ when /^feed:\//i
+ uri.sub!(/^feed:\/+/i, "feed://")
+ when %r[^file:/{4}]i
+ uri.sub!(%r[^file:/+]i, "file:////")
+ when %r[^file://localhost/]i
+ uri.sub!(%r[^file://localhost/+]i, "file:///")
+ when %r[^file:/+]i
+ uri.sub!(%r[^file:/+]i, "file:///")
+ when /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/
+ uri.sub!(/^/, hints[:scheme] + "://")
+ when /\A\d+\..*:\d+\z/
+ uri = "#{hints[:scheme]}://#{uri}"
+ end
+ match = uri.match(URIREGEX)
+ fragments = match.captures
+ authority = fragments[3]
+ if authority && authority.length > 0
+ new_authority = authority.tr("\\", "/").gsub(" ", "%20")
+ # NOTE: We want offset 4, not 3!
+ offset = match.offset(4)
+ uri = uri.dup
+ uri[offset[0]...offset[1]] = new_authority
+ end
+ parsed = self.parse(uri)
+ if parsed.scheme =~ /^[^\/?#\.]+\.[^\/?#]+$/
+ parsed = self.parse(hints[:scheme] + "://" + uri)
+ end
+ if parsed.path.include?(".")
+ if parsed.path[/\b@\b/]
+ parsed.scheme = "mailto" unless parsed.scheme
+ elsif new_host = parsed.path[/^([^\/]+\.[^\/]*)/, 1]
+ parsed.defer_validation do
+ new_path = parsed.path.sub(
+ Regexp.new("^" + Regexp.escape(new_host)), EMPTY_STR)
+ parsed.host = new_host
+ parsed.path = new_path
+ parsed.scheme = hints[:scheme] unless parsed.scheme
+ end
+ end
+ end
+ return parsed
+ end
+
+ ##
+ # Converts a path to a file scheme URI. If the path supplied is
+ # relative, it will be returned as a relative URI. If the path supplied
+ # is actually a non-file URI, it will parse the URI as if it had been
+ # parsed with Addressable::URI.parse. Handles all of the
+ # various Microsoft-specific formats for specifying paths.
+ #
+ # @param [String, Addressable::URI, #to_str] path
+ # Typically a String path to a file or directory, but
+ # will return a sensible return value if an absolute URI is supplied
+ # instead.
+ #
+ # @return [Addressable::URI]
+ # The parsed file scheme URI or the original URI if some other URI
+ # scheme was provided.
+ #
+ # @example
+ # base = Addressable::URI.convert_path("/absolute/path/")
+ # uri = Addressable::URI.convert_path("relative/path")
+ # (base + uri).to_s
+ # #=> "file:///absolute/path/relative/path"
+ #
+ # Addressable::URI.convert_path(
+ # "c:\\windows\\My Documents 100%20\\foo.txt"
+ # ).to_s
+ # #=> "file:///c:/windows/My%20Documents%20100%20/foo.txt"
+ #
+ # Addressable::URI.convert_path("http://example.com/").to_s
+ # #=> "http://example.com/"
+ def self.convert_path(path)
+ # If we were given nil, return nil.
+ return nil unless path
+ # If a URI object is passed, just return itself.
+ return path if path.kind_of?(self)
+ if !path.respond_to?(:to_str)
+ raise TypeError, "Can't convert #{path.class} into String."
+ end
+ # Otherwise, convert to a String
+ path = path.to_str.strip
+
+ path.sub!(/^file:\/?\/?/, EMPTY_STR) if path =~ /^file:\/?\/?/
+ path = SLASH + path if path =~ /^([a-zA-Z])[\|:]/
+ uri = self.parse(path)
+
+ if uri.scheme == nil
+ # Adjust windows-style uris
+ uri.path.sub!(/^\/?([a-zA-Z])[\|:][\\\/]/) do
+ "/#{$1.downcase}:/"
+ end
+ uri.path.tr!("\\", SLASH)
+ if File.exist?(uri.path) &&
+ File.stat(uri.path).directory?
+ uri.path.chomp!(SLASH)
+ uri.path = uri.path + '/'
+ end
+
+ # If the path is absolute, set the scheme and host.
+ if uri.path.start_with?(SLASH)
+ uri.scheme = "file"
+ uri.host = EMPTY_STR
+ end
+ uri.normalize!
+ end
+
+ return uri
+ end
+
+ ##
+ # Joins several URIs together.
+ #
+ # @param [String, Addressable::URI, #to_str] *uris
+ # The URIs to join.
+ #
+ # @return [Addressable::URI] The joined URI.
+ #
+ # @example
+ # base = "http://example.com/"
+ # uri = Addressable::URI.parse("relative/path")
+ # Addressable::URI.join(base, uri)
+ # #=> #String
+ # is passed, the String must be formatted as a regular
+ # expression character class. (Do not include the surrounding square
+ # brackets.) For example, "b-zB-Z0-9" would cause
+ # everything but the letters 'b' through 'z' and the numbers '0' through
+ # '9' to be percent encoded. If a Regexp is passed, the
+ # value /[^b-zB-Z0-9]/ would have the same effect. A set of
+ # useful String values may be found in the
+ # Addressable::URI::CharacterClasses module. The default
+ # value is the reserved plus unreserved character classes specified in
+ # RFC 3986.
+ #
+ # @param [Regexp] upcase_encoded
+ # A string of characters that may already be percent encoded, and whose
+ # encodings should be upcased. This allows normalization of percent
+ # encodings for characters not included in the
+ # character_class.
+ #
+ # @return [String] The encoded component.
+ #
+ # @example
+ # Addressable::URI.encode_component("simple/example", "b-zB-Z0-9")
+ # => "simple%2Fex%61mple"
+ # Addressable::URI.encode_component("simple/example", /[^b-zB-Z0-9]/)
+ # => "simple%2Fex%61mple"
+ # Addressable::URI.encode_component(
+ # "simple/example", Addressable::URI::CharacterClasses::UNRESERVED
+ # )
+ # => "simple%2Fexample"
+ def self.encode_component(component, character_class=
+ CharacterClasses::RESERVED + CharacterClasses::UNRESERVED,
+ upcase_encoded='')
+ return nil if component.nil?
+
+ begin
+ if component.kind_of?(Symbol) ||
+ component.kind_of?(Numeric) ||
+ component.kind_of?(TrueClass) ||
+ component.kind_of?(FalseClass)
+ component = component.to_s
+ else
+ component = component.to_str
+ end
+ rescue TypeError, NoMethodError
+ raise TypeError, "Can't convert #{component.class} into String."
+ end if !component.is_a? String
+
+ if ![String, Regexp].include?(character_class.class)
+ raise TypeError,
+ "Expected String or Regexp, got #{character_class.inspect}"
+ end
+ if character_class.kind_of?(String)
+ character_class = /[^#{character_class}]/
+ end
+ # We can't perform regexps on invalid UTF sequences, but
+ # here we need to, so switch to ASCII.
+ component = component.dup
+ component.force_encoding(Encoding::ASCII_8BIT)
+ # Avoiding gsub! because there are edge cases with frozen strings
+ component = component.gsub(character_class) do |sequence|
+ SEQUENCE_UPCASED_PERCENT_ENCODING_TABLE[sequence]
+ end
+ if upcase_encoded.length > 0
+ upcase_encoded_chars = upcase_encoded.chars.map do |char|
+ SEQUENCE_ENCODING_TABLE[char]
+ end
+ component = component.gsub(/%(#{upcase_encoded_chars.join('|')})/,
+ &:upcase)
+ end
+ return component
+ end
+
+ class << self
+ alias_method :escape_component, :encode_component
+ end
+
+ ##
+ # Unencodes any percent encoded characters within a URI component.
+ # This method may be used for unencoding either components or full URIs,
+ # however, it is recommended to use the unencode_component
+ # alias when unencoding components.
+ #
+ # @param [String, Addressable::URI, #to_str] uri
+ # The URI or component to unencode.
+ #
+ # @param [Class] return_type
+ # The type of object to return.
+ # This value may only be set to String or
+ # Addressable::URI. All other values are invalid. Defaults
+ # to String.
+ #
+ # @param [String] leave_encoded
+ # A string of characters to leave encoded. If a percent encoded character
+ # in this list is encountered then it will remain percent encoded.
+ #
+ # @return [String, Addressable::URI]
+ # The unencoded component or URI.
+ # The return type is determined by the return_type
+ # parameter.
+ def self.unencode(uri, return_type=String, leave_encoded='')
+ return nil if uri.nil?
+
+ begin
+ uri = uri.to_str
+ rescue NoMethodError, TypeError
+ raise TypeError, "Can't convert #{uri.class} into String."
+ end if !uri.is_a? String
+ if ![String, ::Addressable::URI].include?(return_type)
+ raise TypeError,
+ "Expected Class (String or Addressable::URI), " +
+ "got #{return_type.inspect}"
+ end
+
+ result = uri.gsub(/%[0-9a-f]{2}/i) do |sequence|
+ c = sequence[1..3].to_i(16).chr
+ c.force_encoding(sequence.encoding)
+ leave_encoded.include?(c) ? sequence : c
+ end
+
+ result.force_encoding("utf-8")
+ if return_type == String
+ return result
+ elsif return_type == ::Addressable::URI
+ return ::Addressable::URI.parse(result)
+ end
+ end
+
+ class << self
+ alias_method :unescape, :unencode
+ alias_method :unencode_component, :unencode
+ alias_method :unescape_component, :unencode
+ end
+
+
+ ##
+ # Normalizes the encoding of a URI component.
+ #
+ # @param [String, #to_str] component The URI component to encode.
+ #
+ # @param [String, Regexp] character_class
+ # The characters which are not percent encoded. If a String
+ # is passed, the String must be formatted as a regular
+ # expression character class. (Do not include the surrounding square
+ # brackets.) For example, "b-zB-Z0-9" would cause
+ # everything but the letters 'b' through 'z' and the numbers '0'
+ # through '9' to be percent encoded. If a Regexp is passed,
+ # the value /[^b-zB-Z0-9]/ would have the same effect. A
+ # set of useful String values may be found in the
+ # Addressable::URI::CharacterClasses module. The default
+ # value is the reserved plus unreserved character classes specified in
+ # RFC 3986.
+ #
+ # @param [String] leave_encoded
+ # When character_class is a String then
+ # leave_encoded is a string of characters that should remain
+ # percent encoded while normalizing the component; if they appear percent
+ # encoded in the original component, then they will be upcased ("%2f"
+ # normalized to "%2F") but otherwise left alone.
+ #
+ # @return [String] The normalized component.
+ #
+ # @example
+ # Addressable::URI.normalize_component("simpl%65/%65xampl%65", "b-zB-Z")
+ # => "simple%2Fex%61mple"
+ # Addressable::URI.normalize_component(
+ # "simpl%65/%65xampl%65", /[^b-zB-Z]/
+ # )
+ # => "simple%2Fex%61mple"
+ # Addressable::URI.normalize_component(
+ # "simpl%65/%65xampl%65",
+ # Addressable::URI::CharacterClasses::UNRESERVED
+ # )
+ # => "simple%2Fexample"
+ # Addressable::URI.normalize_component(
+ # "one%20two%2fthree%26four",
+ # "0-9a-zA-Z &/",
+ # "/"
+ # )
+ # => "one two%2Fthree&four"
+ def self.normalize_component(component, character_class=
+ CharacterClasses::RESERVED + CharacterClasses::UNRESERVED,
+ leave_encoded='')
+ return nil if component.nil?
+
+ begin
+ component = component.to_str
+ rescue NoMethodError, TypeError
+ raise TypeError, "Can't convert #{component.class} into String."
+ end if !component.is_a? String
+
+ if ![String, Regexp].include?(character_class.class)
+ raise TypeError,
+ "Expected String or Regexp, got #{character_class.inspect}"
+ end
+ if character_class.kind_of?(String)
+ leave_re = if leave_encoded.length > 0
+ character_class = "#{character_class}%" unless character_class.include?('%')
+
+ "|%(?!#{leave_encoded.chars.flat_map do |char|
+ seq = SEQUENCE_ENCODING_TABLE[char]
+ [seq.upcase, seq.downcase]
+ end.join('|')})"
+ end
+
+ character_class = if leave_re
+ /[^#{character_class}]#{leave_re}/
+ else
+ /[^#{character_class}]/
+ end
+ end
+ # We can't perform regexps on invalid UTF sequences, but
+ # here we need to, so switch to ASCII.
+ component = component.dup
+ component.force_encoding(Encoding::ASCII_8BIT)
+ unencoded = self.unencode_component(component, String, leave_encoded)
+ begin
+ encoded = self.encode_component(
+ Addressable::IDNA.unicode_normalize_kc(unencoded),
+ character_class,
+ leave_encoded
+ )
+ rescue ArgumentError
+ encoded = self.encode_component(unencoded)
+ end
+ encoded.force_encoding(Encoding::UTF_8)
+ return encoded
+ end
+
+ ##
+ # Percent encodes any special characters in the URI.
+ #
+ # @param [String, Addressable::URI, #to_str] uri
+ # The URI to encode.
+ #
+ # @param [Class] return_type
+ # The type of object to return.
+ # This value may only be set to String or
+ # Addressable::URI. All other values are invalid. Defaults
+ # to String.
+ #
+ # @return [String, Addressable::URI]
+ # The encoded URI.
+ # The return type is determined by the return_type
+ # parameter.
+ def self.encode(uri, return_type=String)
+ return nil if uri.nil?
+
+ begin
+ uri = uri.to_str
+ rescue NoMethodError, TypeError
+ raise TypeError, "Can't convert #{uri.class} into String."
+ end if !uri.is_a? String
+
+ if ![String, ::Addressable::URI].include?(return_type)
+ raise TypeError,
+ "Expected Class (String or Addressable::URI), " +
+ "got #{return_type.inspect}"
+ end
+ uri_object = uri.kind_of?(self) ? uri : self.parse(uri)
+ encoded_uri = Addressable::URI.new(
+ :scheme => self.encode_component(uri_object.scheme,
+ Addressable::URI::CharacterClasses::SCHEME),
+ :authority => self.encode_component(uri_object.authority,
+ Addressable::URI::CharacterClasses::AUTHORITY),
+ :path => self.encode_component(uri_object.path,
+ Addressable::URI::CharacterClasses::PATH),
+ :query => self.encode_component(uri_object.query,
+ Addressable::URI::CharacterClasses::QUERY),
+ :fragment => self.encode_component(uri_object.fragment,
+ Addressable::URI::CharacterClasses::FRAGMENT)
+ )
+ if return_type == String
+ return encoded_uri.to_s
+ elsif return_type == ::Addressable::URI
+ return encoded_uri
+ end
+ end
+
+ class << self
+ alias_method :escape, :encode
+ end
+
+ ##
+ # Normalizes the encoding of a URI. Characters within a hostname are
+ # not percent encoded to allow for internationalized domain names.
+ #
+ # @param [String, Addressable::URI, #to_str] uri
+ # The URI to encode.
+ #
+ # @param [Class] return_type
+ # The type of object to return.
+ # This value may only be set to String or
+ # Addressable::URI. All other values are invalid. Defaults
+ # to String.
+ #
+ # @return [String, Addressable::URI]
+ # The encoded URI.
+ # The return type is determined by the return_type
+ # parameter.
+ def self.normalized_encode(uri, return_type=String)
+ begin
+ uri = uri.to_str
+ rescue NoMethodError, TypeError
+ raise TypeError, "Can't convert #{uri.class} into String."
+ end if !uri.is_a? String
+
+ if ![String, ::Addressable::URI].include?(return_type)
+ raise TypeError,
+ "Expected Class (String or Addressable::URI), " +
+ "got #{return_type.inspect}"
+ end
+ uri_object = uri.kind_of?(self) ? uri : self.parse(uri)
+ components = {
+ :scheme => self.unencode_component(uri_object.scheme),
+ :user => self.unencode_component(uri_object.user),
+ :password => self.unencode_component(uri_object.password),
+ :host => self.unencode_component(uri_object.host),
+ :port => (uri_object.port.nil? ? nil : uri_object.port.to_s),
+ :path => self.unencode_component(uri_object.path),
+ :query => self.unencode_component(uri_object.query),
+ :fragment => self.unencode_component(uri_object.fragment)
+ }
+ components.each do |key, value|
+ if value != nil
+ begin
+ components[key] =
+ Addressable::IDNA.unicode_normalize_kc(value.to_str)
+ rescue ArgumentError
+ # Likely a malformed UTF-8 character, skip unicode normalization
+ components[key] = value.to_str
+ end
+ end
+ end
+ encoded_uri = Addressable::URI.new(
+ :scheme => self.encode_component(components[:scheme],
+ Addressable::URI::CharacterClasses::SCHEME),
+ :user => self.encode_component(components[:user],
+ Addressable::URI::CharacterClasses::UNRESERVED),
+ :password => self.encode_component(components[:password],
+ Addressable::URI::CharacterClasses::UNRESERVED),
+ :host => components[:host],
+ :port => components[:port],
+ :path => self.encode_component(components[:path],
+ Addressable::URI::CharacterClasses::PATH),
+ :query => self.encode_component(components[:query],
+ Addressable::URI::CharacterClasses::QUERY),
+ :fragment => self.encode_component(components[:fragment],
+ Addressable::URI::CharacterClasses::FRAGMENT)
+ )
+ if return_type == String
+ return encoded_uri.to_s
+ elsif return_type == ::Addressable::URI
+ return encoded_uri
+ end
+ end
+
+ ##
+ # Encodes a set of key/value pairs according to the rules for the
+ # application/x-www-form-urlencoded MIME type.
+ #
+ # @param [#to_hash, #to_ary] form_values
+ # The form values to encode.
+ #
+ # @param [TrueClass, FalseClass] sort
+ # Sort the key/value pairs prior to encoding.
+ # Defaults to false.
+ #
+ # @return [String]
+ # The encoded value.
+ def self.form_encode(form_values, sort=false)
+ if form_values.respond_to?(:to_hash)
+ form_values = form_values.to_hash.to_a
+ elsif form_values.respond_to?(:to_ary)
+ form_values = form_values.to_ary
+ else
+ raise TypeError, "Can't convert #{form_values.class} into Array."
+ end
+
+ form_values = form_values.inject([]) do |accu, (key, value)|
+ if value.kind_of?(Array)
+ value.each do |v|
+ accu << [key.to_s, v.to_s]
+ end
+ else
+ accu << [key.to_s, value.to_s]
+ end
+ accu
+ end
+
+ if sort
+ # Useful for OAuth and optimizing caching systems
+ form_values = form_values.sort
+ end
+ escaped_form_values = form_values.map do |(key, value)|
+ # Line breaks are CRLF pairs
+ [
+ self.encode_component(
+ key.gsub(/(\r\n|\n|\r)/, "\r\n"),
+ CharacterClasses::UNRESERVED
+ ).gsub("%20", "+"),
+ self.encode_component(
+ value.gsub(/(\r\n|\n|\r)/, "\r\n"),
+ CharacterClasses::UNRESERVED
+ ).gsub("%20", "+")
+ ]
+ end
+ return escaped_form_values.map do |(key, value)|
+ "#{key}=#{value}"
+ end.join("&")
+ end
+
+ ##
+ # Decodes a String according to the rules for the
+ # application/x-www-form-urlencoded MIME type.
+ #
+ # @param [String, #to_str] encoded_value
+ # The form values to decode.
+ #
+ # @return [Array]
+ # The decoded values.
+ # This is not a Hash because of the possibility for
+ # duplicate keys.
+ def self.form_unencode(encoded_value)
+ if !encoded_value.respond_to?(:to_str)
+ raise TypeError, "Can't convert #{encoded_value.class} into String."
+ end
+ encoded_value = encoded_value.to_str
+ split_values = encoded_value.split("&").map do |pair|
+ pair.split("=", 2)
+ end
+ return split_values.map do |(key, value)|
+ [
+ key ? self.unencode_component(
+ key.gsub("+", "%20")).gsub(/(\r\n|\n|\r)/, "\n") : nil,
+ value ? (self.unencode_component(
+ value.gsub("+", "%20")).gsub(/(\r\n|\n|\r)/, "\n")) : nil
+ ]
+ end
+ end
+
+ ##
+ # Creates a new uri object from component parts.
+ #
+ # @option [String, #to_str] scheme The scheme component.
+ # @option [String, #to_str] user The user component.
+ # @option [String, #to_str] password The password component.
+ # @option [String, #to_str] userinfo
+ # The userinfo component. If this is supplied, the user and password
+ # components must be omitted.
+ # @option [String, #to_str] host The host component.
+ # @option [String, #to_str] port The port component.
+ # @option [String, #to_str] authority
+ # The authority component. If this is supplied, the user, password,
+ # userinfo, host, and port components must be omitted.
+ # @option [String, #to_str] path The path component.
+ # @option [String, #to_str] query The query component.
+ # @option [String, #to_str] fragment The fragment component.
+ #
+ # @return [Addressable::URI] The constructed URI object.
+ def initialize(options={})
+ if options.has_key?(:authority)
+ if (options.keys & [:userinfo, :user, :password, :host, :port]).any?
+ raise ArgumentError,
+ "Cannot specify both an authority and any of the components " +
+ "within the authority."
+ end
+ end
+ if options.has_key?(:userinfo)
+ if (options.keys & [:user, :password]).any?
+ raise ArgumentError,
+ "Cannot specify both a userinfo and either the user or password."
+ end
+ end
+
+ self.defer_validation do
+ # Bunch of crazy logic required because of the composite components
+ # like userinfo and authority.
+ self.scheme = options[:scheme] if options[:scheme]
+ self.user = options[:user] if options[:user]
+ self.password = options[:password] if options[:password]
+ self.userinfo = options[:userinfo] if options[:userinfo]
+ self.host = options[:host] if options[:host]
+ self.port = options[:port] if options[:port]
+ self.authority = options[:authority] if options[:authority]
+ self.path = options[:path] if options[:path]
+ self.query = options[:query] if options[:query]
+ self.query_values = options[:query_values] if options[:query_values]
+ self.fragment = options[:fragment] if options[:fragment]
+ end
+ self.to_s
+ end
+
+ ##
+ # Freeze URI, initializing instance variables.
+ #
+ # @return [Addressable::URI] The frozen URI object.
+ def freeze
+ self.normalized_scheme
+ self.normalized_user
+ self.normalized_password
+ self.normalized_userinfo
+ self.normalized_host
+ self.normalized_port
+ self.normalized_authority
+ self.normalized_site
+ self.normalized_path
+ self.normalized_query
+ self.normalized_fragment
+ self.hash
+ super
+ end
+
+ ##
+ # The scheme component for this URI.
+ #
+ # @return [String] The scheme component.
+ def scheme
+ return defined?(@scheme) ? @scheme : nil
+ end
+
+ ##
+ # The scheme component for this URI, normalized.
+ #
+ # @return [String] The scheme component, normalized.
+ def normalized_scheme
+ return nil unless self.scheme
+ @normalized_scheme ||= begin
+ if self.scheme =~ /^\s*ssh\+svn\s*$/i
+ "svn+ssh".dup
+ else
+ Addressable::URI.normalize_component(
+ self.scheme.strip.downcase,
+ Addressable::URI::NormalizeCharacterClasses::SCHEME
+ )
+ end
+ end
+ # All normalized values should be UTF-8
+ force_utf8_encoding_if_needed(@normalized_scheme)
+ @normalized_scheme
+ end
+
+ ##
+ # Sets the scheme component for this URI.
+ #
+ # @param [String, #to_str] new_scheme The new scheme component.
+ def scheme=(new_scheme)
+ if new_scheme && !new_scheme.respond_to?(:to_str)
+ raise TypeError, "Can't convert #{new_scheme.class} into String."
+ elsif new_scheme
+ new_scheme = new_scheme.to_str
+ end
+ if new_scheme && new_scheme !~ /\A[a-z][a-z0-9\.\+\-]*\z/i
+ raise InvalidURIError, "Invalid scheme format: '#{new_scheme}'"
+ end
+ @scheme = new_scheme
+ @scheme = nil if @scheme.to_s.strip.empty?
+
+ # Reset dependent values
+ remove_instance_variable(:@normalized_scheme) if defined?(@normalized_scheme)
+ remove_composite_values
+
+ # Ensure we haven't created an invalid URI
+ validate()
+ end
+
+ ##
+ # The user component for this URI.
+ #
+ # @return [String] The user component.
+ def user
+ return defined?(@user) ? @user : nil
+ end
+
+ ##
+ # The user component for this URI, normalized.
+ #
+ # @return [String] The user component, normalized.
+ def normalized_user
+ return nil unless self.user
+ return @normalized_user if defined?(@normalized_user)
+ @normalized_user ||= begin
+ if normalized_scheme =~ /https?/ && self.user.strip.empty? &&
+ (!self.password || self.password.strip.empty?)
+ nil
+ else
+ Addressable::URI.normalize_component(
+ self.user.strip,
+ Addressable::URI::NormalizeCharacterClasses::UNRESERVED
+ )
+ end
+ end
+ # All normalized values should be UTF-8
+ force_utf8_encoding_if_needed(@normalized_user)
+ @normalized_user
+ end
+
+ ##
+ # Sets the user component for this URI.
+ #
+ # @param [String, #to_str] new_user The new user component.
+ def user=(new_user)
+ if new_user && !new_user.respond_to?(:to_str)
+ raise TypeError, "Can't convert #{new_user.class} into String."
+ end
+ @user = new_user ? new_user.to_str : nil
+
+ # You can't have a nil user with a non-nil password
+ if password != nil
+ @user = EMPTY_STR if @user.nil?
+ end
+
+ # Reset dependent values
+ remove_instance_variable(:@userinfo) if defined?(@userinfo)
+ remove_instance_variable(:@normalized_userinfo) if defined?(@normalized_userinfo)
+ remove_instance_variable(:@authority) if defined?(@authority)
+ remove_instance_variable(:@normalized_user) if defined?(@normalized_user)
+ remove_composite_values
+
+ # Ensure we haven't created an invalid URI
+ validate()
+ end
+
+ ##
+ # The password component for this URI.
+ #
+ # @return [String] The password component.
+ def password
+ return defined?(@password) ? @password : nil
+ end
+
+ ##
+ # The password component for this URI, normalized.
+ #
+ # @return [String] The password component, normalized.
+ def normalized_password
+ return nil unless self.password
+ return @normalized_password if defined?(@normalized_password)
+ @normalized_password ||= begin
+ if self.normalized_scheme =~ /https?/ && self.password.strip.empty? &&
+ (!self.user || self.user.strip.empty?)
+ nil
+ else
+ Addressable::URI.normalize_component(
+ self.password.strip,
+ Addressable::URI::NormalizeCharacterClasses::UNRESERVED
+ )
+ end
+ end
+ # All normalized values should be UTF-8
+ force_utf8_encoding_if_needed(@normalized_password)
+ @normalized_password
+ end
+
+ ##
+ # Sets the password component for this URI.
+ #
+ # @param [String, #to_str] new_password The new password component.
+ def password=(new_password)
+ if new_password && !new_password.respond_to?(:to_str)
+ raise TypeError, "Can't convert #{new_password.class} into String."
+ end
+ @password = new_password ? new_password.to_str : nil
+
+ # You can't have a nil user with a non-nil password
+ @password ||= nil
+ @user ||= nil
+ if @password != nil
+ @user = EMPTY_STR if @user.nil?
+ end
+
+ # Reset dependent values
+ remove_instance_variable(:@userinfo) if defined?(@userinfo)
+ remove_instance_variable(:@normalized_userinfo) if defined?(@normalized_userinfo)
+ remove_instance_variable(:@authority) if defined?(@authority)
+ remove_instance_variable(:@normalized_password) if defined?(@normalized_password)
+ remove_composite_values
+
+ # Ensure we haven't created an invalid URI
+ validate()
+ end
+
+ ##
+ # The userinfo component for this URI.
+ # Combines the user and password components.
+ #
+ # @return [String] The userinfo component.
+ def userinfo
+ current_user = self.user
+ current_password = self.password
+ (current_user || current_password) && @userinfo ||= begin
+ if current_user && current_password
+ "#{current_user}:#{current_password}"
+ elsif current_user && !current_password
+ "#{current_user}"
+ end
+ end
+ end
+
+ ##
+ # The userinfo component for this URI, normalized.
+ #
+ # @return [String] The userinfo component, normalized.
+ def normalized_userinfo
+ return nil unless self.userinfo
+ return @normalized_userinfo if defined?(@normalized_userinfo)
+ @normalized_userinfo ||= begin
+ current_user = self.normalized_user
+ current_password = self.normalized_password
+ if !current_user && !current_password
+ nil
+ elsif current_user && current_password
+ "#{current_user}:#{current_password}".dup
+ elsif current_user && !current_password
+ "#{current_user}".dup
+ end
+ end
+ # All normalized values should be UTF-8
+ force_utf8_encoding_if_needed(@normalized_userinfo)
+ @normalized_userinfo
+ end
+
+ ##
+ # Sets the userinfo component for this URI.
+ #
+ # @param [String, #to_str] new_userinfo The new userinfo component.
+ def userinfo=(new_userinfo)
+ if new_userinfo && !new_userinfo.respond_to?(:to_str)
+ raise TypeError, "Can't convert #{new_userinfo.class} into String."
+ end
+ new_user, new_password = if new_userinfo
+ [
+ new_userinfo.to_str.strip[/^(.*):/, 1],
+ new_userinfo.to_str.strip[/:(.*)$/, 1]
+ ]
+ else
+ [nil, nil]
+ end
+
+ # Password assigned first to ensure validity in case of nil
+ self.password = new_password
+ self.user = new_user
+
+ # Reset dependent values
+ remove_instance_variable(:@authority) if defined?(@authority)
+ remove_composite_values
+
+ # Ensure we haven't created an invalid URI
+ validate()
+ end
+
+ ##
+ # The host component for this URI.
+ #
+ # @return [String] The host component.
+ def host
+ return defined?(@host) ? @host : nil
+ end
+
+ ##
+ # The host component for this URI, normalized.
+ #
+ # @return [String] The host component, normalized.
+ def normalized_host
+ return nil unless self.host
+
+ @normalized_host ||= begin
+ if !self.host.strip.empty?
+ result = ::Addressable::IDNA.to_ascii(
+ URI.unencode_component(self.host.strip.downcase)
+ )
+ if result =~ /[^\.]\.$/
+ # Single trailing dots are unnecessary.
+ result = result[0...-1]
+ end
+ result = Addressable::URI.normalize_component(
+ result,
+ NormalizeCharacterClasses::HOST
+ )
+ result
+ else
+ EMPTY_STR.dup
+ end
+ end
+ # All normalized values should be UTF-8
+ force_utf8_encoding_if_needed(@normalized_host)
+ @normalized_host
+ end
+
+ ##
+ # Sets the host component for this URI.
+ #
+ # @param [String, #to_str] new_host The new host component.
+ def host=(new_host)
+ if new_host && !new_host.respond_to?(:to_str)
+ raise TypeError, "Can't convert #{new_host.class} into String."
+ end
+ @host = new_host ? new_host.to_str : nil
+
+ # Reset dependent values
+ remove_instance_variable(:@authority) if defined?(@authority)
+ remove_instance_variable(:@normalized_host) if defined?(@normalized_host)
+ remove_composite_values
+
+ # Ensure we haven't created an invalid URI
+ validate()
+ end
+
+ ##
+ # This method is same as URI::Generic#host except
+ # brackets for IPv6 (and 'IPvFuture') addresses are removed.
+ #
+ # @see Addressable::URI#host
+ #
+ # @return [String] The hostname for this URI.
+ def hostname
+ v = self.host
+ /\A\[(.*)\]\z/ =~ v ? $1 : v
+ end
+
+ ##
+ # This method is same as URI::Generic#host= except
+ # the argument can be a bare IPv6 address (or 'IPvFuture').
+ #
+ # @see Addressable::URI#host=
+ #
+ # @param [String, #to_str] new_hostname The new hostname for this URI.
+ def hostname=(new_hostname)
+ if new_hostname &&
+ (new_hostname.respond_to?(:ipv4?) || new_hostname.respond_to?(:ipv6?))
+ new_hostname = new_hostname.to_s
+ elsif new_hostname && !new_hostname.respond_to?(:to_str)
+ raise TypeError, "Can't convert #{new_hostname.class} into String."
+ end
+ v = new_hostname ? new_hostname.to_str : nil
+ v = "[#{v}]" if /\A\[.*\]\z/ !~ v && /:/ =~ v
+ self.host = v
+ end
+
+ ##
+ # Returns the top-level domain for this host.
+ #
+ # @example
+ # Addressable::URI.parse("http://www.example.co.uk").tld # => "co.uk"
+ def tld
+ PublicSuffix.parse(self.host, ignore_private: true).tld
+ end
+
+ ##
+ # Sets the top-level domain for this URI.
+ #
+ # @param [String, #to_str] new_tld The new top-level domain.
+ def tld=(new_tld)
+ replaced_tld = host.sub(/#{tld}\z/, new_tld)
+ self.host = PublicSuffix::Domain.new(replaced_tld).to_s
+ end
+
+ ##
+ # Returns the public suffix domain for this host.
+ #
+ # @example
+ # Addressable::URI.parse("http://www.example.co.uk").domain # => "example.co.uk"
+ def domain
+ PublicSuffix.domain(self.host, ignore_private: true)
+ end
+
+ ##
+ # The authority component for this URI.
+ # Combines the user, password, host, and port components.
+ #
+ # @return [String] The authority component.
+ def authority
+ self.host && @authority ||= begin
+ authority = String.new
+ if self.userinfo != nil
+ authority << "#{self.userinfo}@"
+ end
+ authority << self.host
+ if self.port != nil
+ authority << ":#{self.port}"
+ end
+ authority
+ end
+ end
+
+ ##
+ # The authority component for this URI, normalized.
+ #
+ # @return [String] The authority component, normalized.
+ def normalized_authority
+ return nil unless self.authority
+ @normalized_authority ||= begin
+ authority = String.new
+ if self.normalized_userinfo != nil
+ authority << "#{self.normalized_userinfo}@"
+ end
+ authority << self.normalized_host
+ if self.normalized_port != nil
+ authority << ":#{self.normalized_port}"
+ end
+ authority
+ end
+ # All normalized values should be UTF-8
+ force_utf8_encoding_if_needed(@normalized_authority)
+ @normalized_authority
+ end
+
+ ##
+ # Sets the authority component for this URI.
+ #
+ # @param [String, #to_str] new_authority The new authority component.
+ def authority=(new_authority)
+ if new_authority
+ if !new_authority.respond_to?(:to_str)
+ raise TypeError, "Can't convert #{new_authority.class} into String."
+ end
+ new_authority = new_authority.to_str
+ new_userinfo = new_authority[/^([^\[\]]*)@/, 1]
+ if new_userinfo
+ new_user = new_userinfo.strip[/^([^:]*):?/, 1]
+ new_password = new_userinfo.strip[/:(.*)$/, 1]
+ end
+ new_host = new_authority.sub(
+ /^([^\[\]]*)@/, EMPTY_STR
+ ).sub(
+ /:([^:@\[\]]*?)$/, EMPTY_STR
+ )
+ new_port =
+ new_authority[/:([^:@\[\]]*?)$/, 1]
+ end
+
+ # Password assigned first to ensure validity in case of nil
+ self.password = defined?(new_password) ? new_password : nil
+ self.user = defined?(new_user) ? new_user : nil
+ self.host = defined?(new_host) ? new_host : nil
+ self.port = defined?(new_port) ? new_port : nil
+
+ # Reset dependent values
+ remove_instance_variable(:@userinfo) if defined?(@userinfo)
+ remove_instance_variable(:@normalized_userinfo) if defined?(@normalized_userinfo)
+ remove_composite_values
+
+ # Ensure we haven't created an invalid URI
+ validate()
+ end
+
+ ##
+ # The origin for this URI, serialized to ASCII, as per
+ # RFC 6454, section 6.2.
+ #
+ # @return [String] The serialized origin.
+ def origin
+ if self.scheme && self.authority
+ if self.normalized_port
+ "#{self.normalized_scheme}://#{self.normalized_host}" +
+ ":#{self.normalized_port}"
+ else
+ "#{self.normalized_scheme}://#{self.normalized_host}"
+ end
+ else
+ "null"
+ end
+ end
+
+ ##
+ # Sets the origin for this URI, serialized to ASCII, as per
+ # RFC 6454, section 6.2. This assignment will reset the `userinfo`
+ # component.
+ #
+ # @param [String, #to_str] new_origin The new origin component.
+ def origin=(new_origin)
+ if new_origin
+ if !new_origin.respond_to?(:to_str)
+ raise TypeError, "Can't convert #{new_origin.class} into String."
+ end
+ new_origin = new_origin.to_str
+ new_scheme = new_origin[/^([^:\/?#]+):\/\//, 1]
+ unless new_scheme
+ raise InvalidURIError, 'An origin cannot omit the scheme.'
+ end
+ new_host = new_origin[/:\/\/([^\/?#:]+)/, 1]
+ unless new_host
+ raise InvalidURIError, 'An origin cannot omit the host.'
+ end
+ new_port = new_origin[/:([^:@\[\]\/]*?)$/, 1]
+ end
+
+ self.scheme = defined?(new_scheme) ? new_scheme : nil
+ self.host = defined?(new_host) ? new_host : nil
+ self.port = defined?(new_port) ? new_port : nil
+ self.userinfo = nil
+
+ # Reset dependent values
+ remove_instance_variable(:@userinfo) if defined?(@userinfo)
+ remove_instance_variable(:@normalized_userinfo) if defined?(@normalized_userinfo)
+ remove_instance_variable(:@authority) if defined?(@authority)
+ remove_instance_variable(:@normalized_authority) if defined?(@normalized_authority)
+ remove_composite_values
+
+ # Ensure we haven't created an invalid URI
+ validate()
+ end
+
+ # Returns an array of known ip-based schemes. These schemes typically
+ # use a similar URI form:
+ # //:@:/
+ def self.ip_based_schemes
+ return self.port_mapping.keys
+ end
+
+ # Returns a hash of common IP-based schemes and their default port
+ # numbers. Adding new schemes to this hash, as necessary, will allow
+ # for better URI normalization.
+ def self.port_mapping
+ PORT_MAPPING
+ end
+
+ ##
+ # The port component for this URI.
+ # This is the port number actually given in the URI. This does not
+ # infer port numbers from default values.
+ #
+ # @return [Integer] The port component.
+ def port
+ return defined?(@port) ? @port : nil
+ end
+
+ ##
+ # The port component for this URI, normalized.
+ #
+ # @return [Integer] The port component, normalized.
+ def normalized_port
+ return nil unless self.port
+ return @normalized_port if defined?(@normalized_port)
+ @normalized_port ||= begin
+ if URI.port_mapping[self.normalized_scheme] == self.port
+ nil
+ else
+ self.port
+ end
+ end
+ end
+
+ ##
+ # Sets the port component for this URI.
+ #
+ # @param [String, Integer, #to_s] new_port The new port component.
+ def port=(new_port)
+ if new_port != nil && new_port.respond_to?(:to_str)
+ new_port = Addressable::URI.unencode_component(new_port.to_str)
+ end
+
+ if new_port.respond_to?(:valid_encoding?) && !new_port.valid_encoding?
+ raise InvalidURIError, "Invalid encoding in port"
+ end
+
+ if new_port != nil && !(new_port.to_s =~ /^\d+$/)
+ raise InvalidURIError,
+ "Invalid port number: #{new_port.inspect}"
+ end
+
+ @port = new_port.to_s.to_i
+ @port = nil if @port == 0
+
+ # Reset dependent values
+ remove_instance_variable(:@authority) if defined?(@authority)
+ remove_instance_variable(:@normalized_port) if defined?(@normalized_port)
+ remove_composite_values
+
+ # Ensure we haven't created an invalid URI
+ validate()
+ end
+
+ ##
+ # The inferred port component for this URI.
+ # This method will normalize to the default port for the URI's scheme if
+ # the port isn't explicitly specified in the URI.
+ #
+ # @return [Integer] The inferred port component.
+ def inferred_port
+ if self.port.to_i == 0
+ self.default_port
+ else
+ self.port.to_i
+ end
+ end
+
+ ##
+ # The default port for this URI's scheme.
+ # This method will always returns the default port for the URI's scheme
+ # regardless of the presence of an explicit port in the URI.
+ #
+ # @return [Integer] The default port.
+ def default_port
+ URI.port_mapping[self.scheme.strip.downcase] if self.scheme
+ end
+
+ ##
+ # The combination of components that represent a site.
+ # Combines the scheme, user, password, host, and port components.
+ # Primarily useful for HTTP and HTTPS.
+ #
+ # For example, "http://example.com/path?query" would have a
+ # site value of "http://example.com".
+ #
+ # @return [String] The components that identify a site.
+ def site
+ (self.scheme || self.authority) && @site ||= begin
+ site_string = "".dup
+ site_string << "#{self.scheme}:" if self.scheme != nil
+ site_string << "//#{self.authority}" if self.authority != nil
+ site_string
+ end
+ end
+
+ ##
+ # The normalized combination of components that represent a site.
+ # Combines the scheme, user, password, host, and port components.
+ # Primarily useful for HTTP and HTTPS.
+ #
+ # For example, "http://example.com/path?query" would have a
+ # site value of "http://example.com".
+ #
+ # @return [String] The normalized components that identify a site.
+ def normalized_site
+ return nil unless self.site
+ @normalized_site ||= begin
+ site_string = "".dup
+ if self.normalized_scheme != nil
+ site_string << "#{self.normalized_scheme}:"
+ end
+ if self.normalized_authority != nil
+ site_string << "//#{self.normalized_authority}"
+ end
+ site_string
+ end
+ # All normalized values should be UTF-8
+ force_utf8_encoding_if_needed(@normalized_site)
+ @normalized_site
+ end
+
+ ##
+ # Sets the site value for this URI.
+ #
+ # @param [String, #to_str] new_site The new site value.
+ def site=(new_site)
+ if new_site
+ if !new_site.respond_to?(:to_str)
+ raise TypeError, "Can't convert #{new_site.class} into String."
+ end
+ new_site = new_site.to_str
+ # These two regular expressions derived from the primary parsing
+ # expression
+ self.scheme = new_site[/^(?:([^:\/?#]+):)?(?:\/\/(?:[^\/?#]*))?$/, 1]
+ self.authority = new_site[
+ /^(?:(?:[^:\/?#]+):)?(?:\/\/([^\/?#]*))?$/, 1
+ ]
+ else
+ self.scheme = nil
+ self.authority = nil
+ end
+ end
+
+ ##
+ # The path component for this URI.
+ #
+ # @return [String] The path component.
+ def path
+ return defined?(@path) ? @path : EMPTY_STR
+ end
+
+ NORMPATH = /^(?!\/)[^\/:]*:.*$/
+ ##
+ # The path component for this URI, normalized.
+ #
+ # @return [String] The path component, normalized.
+ def normalized_path
+ @normalized_path ||= begin
+ path = self.path.to_s
+ if self.scheme == nil && path =~ NORMPATH
+ # Relative paths with colons in the first segment are ambiguous.
+ path = path.sub(":", "%2F")
+ end
+ # String#split(delimeter, -1) uses the more strict splitting behavior
+ # found by default in Python.
+ result = path.strip.split(SLASH, -1).map do |segment|
+ Addressable::URI.normalize_component(
+ segment,
+ Addressable::URI::NormalizeCharacterClasses::PCHAR
+ )
+ end.join(SLASH)
+
+ result = URI.normalize_path(result)
+ if result.empty? &&
+ ["http", "https", "ftp", "tftp"].include?(self.normalized_scheme)
+ result = SLASH.dup
+ end
+ result
+ end
+ # All normalized values should be UTF-8
+ force_utf8_encoding_if_needed(@normalized_path)
+ @normalized_path
+ end
+
+ ##
+ # Sets the path component for this URI.
+ #
+ # @param [String, #to_str] new_path The new path component.
+ def path=(new_path)
+ if new_path && !new_path.respond_to?(:to_str)
+ raise TypeError, "Can't convert #{new_path.class} into String."
+ end
+ @path = (new_path || EMPTY_STR).to_str
+ if !@path.empty? && @path[0..0] != SLASH && host != nil
+ @path = "/#{@path}"
+ end
+
+ # Reset dependent values
+ remove_instance_variable(:@normalized_path) if defined?(@normalized_path)
+ remove_composite_values
+
+ # Ensure we haven't created an invalid URI
+ validate()
+ end
+
+ ##
+ # The basename, if any, of the file in the path component.
+ #
+ # @return [String] The path's basename.
+ def basename
+ # Path cannot be nil
+ return File.basename(self.path).sub(/;[^\/]*$/, EMPTY_STR)
+ end
+
+ ##
+ # The extname, if any, of the file in the path component.
+ # Empty string if there is no extension.
+ #
+ # @return [String] The path's extname.
+ def extname
+ return nil unless self.path
+ return File.extname(self.basename)
+ end
+
+ ##
+ # The query component for this URI.
+ #
+ # @return [String] The query component.
+ def query
+ return defined?(@query) ? @query : nil
+ end
+
+ ##
+ # The query component for this URI, normalized.
+ #
+ # @return [String] The query component, normalized.
+ def normalized_query(*flags)
+ return nil unless self.query
+ return @normalized_query if defined?(@normalized_query)
+ @normalized_query ||= begin
+ modified_query_class = Addressable::URI::CharacterClasses::QUERY.dup
+ # Make sure possible key-value pair delimiters are escaped.
+ modified_query_class.sub!("\\&", "").sub!("\\;", "")
+ pairs = (query || "").split("&", -1)
+ pairs.delete_if(&:empty?).uniq! if flags.include?(:compacted)
+ pairs.sort! if flags.include?(:sorted)
+ component = pairs.map do |pair|
+ Addressable::URI.normalize_component(
+ pair,
+ Addressable::URI::NormalizeCharacterClasses::QUERY,
+ "+"
+ )
+ end.join("&")
+ component == "" ? nil : component
+ end
+ # All normalized values should be UTF-8
+ force_utf8_encoding_if_needed(@normalized_query)
+ @normalized_query
+ end
+
+ ##
+ # Sets the query component for this URI.
+ #
+ # @param [String, #to_str] new_query The new query component.
+ def query=(new_query)
+ if new_query && !new_query.respond_to?(:to_str)
+ raise TypeError, "Can't convert #{new_query.class} into String."
+ end
+ @query = new_query ? new_query.to_str : nil
+
+ # Reset dependent values
+ remove_instance_variable(:@normalized_query) if defined?(@normalized_query)
+ remove_composite_values
+ end
+
+ ##
+ # Converts the query component to a Hash value.
+ #
+ # @param [Class] return_type The return type desired. Value must be either
+ # `Hash` or `Array`.
+ #
+ # @return [Hash, Array, nil] The query string parsed as a Hash or Array
+ # or nil if the query string is blank.
+ #
+ # @example
+ # Addressable::URI.parse("?one=1&two=2&three=3").query_values
+ # #=> {"one" => "1", "two" => "2", "three" => "3"}
+ # Addressable::URI.parse("?one=two&one=three").query_values(Array)
+ # #=> [["one", "two"], ["one", "three"]]
+ # Addressable::URI.parse("?one=two&one=three").query_values(Hash)
+ # #=> {"one" => "three"}
+ # Addressable::URI.parse("?").query_values
+ # #=> {}
+ # Addressable::URI.parse("").query_values
+ # #=> nil
+ def query_values(return_type=Hash)
+ empty_accumulator = Array == return_type ? [] : {}
+ if return_type != Hash && return_type != Array
+ raise ArgumentError, "Invalid return type. Must be Hash or Array."
+ end
+ return nil if self.query == nil
+ split_query = self.query.split("&").map do |pair|
+ pair.split("=", 2) if pair && !pair.empty?
+ end.compact
+ return split_query.inject(empty_accumulator.dup) do |accu, pair|
+ # I'd rather use key/value identifiers instead of array lookups,
+ # but in this case I really want to maintain the exact pair structure,
+ # so it's best to make all changes in-place.
+ pair[0] = URI.unencode_component(pair[0])
+ if pair[1].respond_to?(:to_str)
+ value = pair[1].to_str
+ # I loathe the fact that I have to do this. Stupid HTML 4.01.
+ # Treating '+' as a space was just an unbelievably bad idea.
+ # There was nothing wrong with '%20'!
+ # If it ain't broke, don't fix it!
+ value = value.tr("+", " ") if ["http", "https", nil].include?(scheme)
+ pair[1] = URI.unencode_component(value)
+ end
+ if return_type == Hash
+ accu[pair[0]] = pair[1]
+ else
+ accu << pair
+ end
+ accu
+ end
+ end
+
+ ##
+ # Sets the query component for this URI from a Hash object.
+ # An empty Hash or Array will result in an empty query string.
+ #
+ # @param [Hash, #to_hash, Array] new_query_values The new query values.
+ #
+ # @example
+ # uri.query_values = {:a => "a", :b => ["c", "d", "e"]}
+ # uri.query
+ # # => "a=a&b=c&b=d&b=e"
+ # uri.query_values = [['a', 'a'], ['b', 'c'], ['b', 'd'], ['b', 'e']]
+ # uri.query
+ # # => "a=a&b=c&b=d&b=e"
+ # uri.query_values = [['a', 'a'], ['b', ['c', 'd', 'e']]]
+ # uri.query
+ # # => "a=a&b=c&b=d&b=e"
+ # uri.query_values = [['flag'], ['key', 'value']]
+ # uri.query
+ # # => "flag&key=value"
+ def query_values=(new_query_values)
+ if new_query_values == nil
+ self.query = nil
+ return nil
+ end
+
+ if !new_query_values.is_a?(Array)
+ if !new_query_values.respond_to?(:to_hash)
+ raise TypeError,
+ "Can't convert #{new_query_values.class} into Hash."
+ end
+ new_query_values = new_query_values.to_hash
+ new_query_values = new_query_values.map do |key, value|
+ key = key.to_s if key.kind_of?(Symbol)
+ [key, value]
+ end
+ # Useful default for OAuth and caching.
+ # Only to be used for non-Array inputs. Arrays should preserve order.
+ new_query_values.sort!
+ end
+
+ # new_query_values have form [['key1', 'value1'], ['key2', 'value2']]
+ buffer = "".dup
+ new_query_values.each do |key, value|
+ encoded_key = URI.encode_component(
+ key, CharacterClasses::UNRESERVED
+ )
+ if value == nil
+ buffer << "#{encoded_key}&"
+ elsif value.kind_of?(Array)
+ value.each do |sub_value|
+ encoded_value = URI.encode_component(
+ sub_value, CharacterClasses::UNRESERVED
+ )
+ buffer << "#{encoded_key}=#{encoded_value}&"
+ end
+ else
+ encoded_value = URI.encode_component(
+ value, CharacterClasses::UNRESERVED
+ )
+ buffer << "#{encoded_key}=#{encoded_value}&"
+ end
+ end
+ self.query = buffer.chop
+ end
+
+ ##
+ # The HTTP request URI for this URI. This is the path and the
+ # query string.
+ #
+ # @return [String] The request URI required for an HTTP request.
+ def request_uri
+ return nil if self.absolute? && self.scheme !~ /^https?$/i
+ return (
+ (!self.path.empty? ? self.path : SLASH) +
+ (self.query ? "?#{self.query}" : EMPTY_STR)
+ )
+ end
+
+ ##
+ # Sets the HTTP request URI for this URI.
+ #
+ # @param [String, #to_str] new_request_uri The new HTTP request URI.
+ def request_uri=(new_request_uri)
+ if !new_request_uri.respond_to?(:to_str)
+ raise TypeError, "Can't convert #{new_request_uri.class} into String."
+ end
+ if self.absolute? && self.scheme !~ /^https?$/i
+ raise InvalidURIError,
+ "Cannot set an HTTP request URI for a non-HTTP URI."
+ end
+ new_request_uri = new_request_uri.to_str
+ path_component = new_request_uri[/^([^\?]*)\??(?:.*)$/, 1]
+ query_component = new_request_uri[/^(?:[^\?]*)\?(.*)$/, 1]
+ path_component = path_component.to_s
+ path_component = (!path_component.empty? ? path_component : SLASH)
+ self.path = path_component
+ self.query = query_component
+
+ # Reset dependent values
+ remove_composite_values
+ end
+
+ ##
+ # The fragment component for this URI.
+ #
+ # @return [String] The fragment component.
+ def fragment
+ return defined?(@fragment) ? @fragment : nil
+ end
+
+ ##
+ # The fragment component for this URI, normalized.
+ #
+ # @return [String] The fragment component, normalized.
+ def normalized_fragment
+ return nil unless self.fragment
+ return @normalized_fragment if defined?(@normalized_fragment)
+ @normalized_fragment ||= begin
+ component = Addressable::URI.normalize_component(
+ self.fragment,
+ Addressable::URI::NormalizeCharacterClasses::FRAGMENT
+ )
+ component == "" ? nil : component
+ end
+ # All normalized values should be UTF-8
+ force_utf8_encoding_if_needed(@normalized_fragment)
+ @normalized_fragment
+ end
+
+ ##
+ # Sets the fragment component for this URI.
+ #
+ # @param [String, #to_str] new_fragment The new fragment component.
+ def fragment=(new_fragment)
+ if new_fragment && !new_fragment.respond_to?(:to_str)
+ raise TypeError, "Can't convert #{new_fragment.class} into String."
+ end
+ @fragment = new_fragment ? new_fragment.to_str : nil
+
+ # Reset dependent values
+ remove_instance_variable(:@normalized_fragment) if defined?(@normalized_fragment)
+ remove_composite_values
+
+ # Ensure we haven't created an invalid URI
+ validate()
+ end
+
+ ##
+ # Determines if the scheme indicates an IP-based protocol.
+ #
+ # @return [TrueClass, FalseClass]
+ # true if the scheme indicates an IP-based protocol.
+ # false otherwise.
+ def ip_based?
+ if self.scheme
+ return URI.ip_based_schemes.include?(
+ self.scheme.strip.downcase)
+ end
+ return false
+ end
+
+ ##
+ # Determines if the URI is relative.
+ #
+ # @return [TrueClass, FalseClass]
+ # true if the URI is relative. false
+ # otherwise.
+ def relative?
+ return self.scheme.nil?
+ end
+
+ ##
+ # Determines if the URI is absolute.
+ #
+ # @return [TrueClass, FalseClass]
+ # true if the URI is absolute. false
+ # otherwise.
+ def absolute?
+ return !relative?
+ end
+
+ ##
+ # Joins two URIs together.
+ #
+ # @param [String, Addressable::URI, #to_str] The URI to join with.
+ #
+ # @return [Addressable::URI] The joined URI.
+ def join(uri)
+ if !uri.respond_to?(:to_str)
+ raise TypeError, "Can't convert #{uri.class} into String."
+ end
+ if !uri.kind_of?(URI)
+ # Otherwise, convert to a String, then parse.
+ uri = URI.parse(uri.to_str)
+ end
+ if uri.to_s.empty?
+ return self.dup
+ end
+
+ joined_scheme = nil
+ joined_user = nil
+ joined_password = nil
+ joined_host = nil
+ joined_port = nil
+ joined_path = nil
+ joined_query = nil
+ joined_fragment = nil
+
+ # Section 5.2.2 of RFC 3986
+ if uri.scheme != nil
+ joined_scheme = uri.scheme
+ joined_user = uri.user
+ joined_password = uri.password
+ joined_host = uri.host
+ joined_port = uri.port
+ joined_path = URI.normalize_path(uri.path)
+ joined_query = uri.query
+ else
+ if uri.authority != nil
+ joined_user = uri.user
+ joined_password = uri.password
+ joined_host = uri.host
+ joined_port = uri.port
+ joined_path = URI.normalize_path(uri.path)
+ joined_query = uri.query
+ else
+ if uri.path == nil || uri.path.empty?
+ joined_path = self.path
+ if uri.query != nil
+ joined_query = uri.query
+ else
+ joined_query = self.query
+ end
+ else
+ if uri.path[0..0] == SLASH
+ joined_path = URI.normalize_path(uri.path)
+ else
+ base_path = self.path.dup
+ base_path = EMPTY_STR if base_path == nil
+ base_path = URI.normalize_path(base_path)
+
+ # Section 5.2.3 of RFC 3986
+ #
+ # Removes the right-most path segment from the base path.
+ if base_path.include?(SLASH)
+ base_path.sub!(/\/[^\/]+$/, SLASH)
+ else
+ base_path = EMPTY_STR
+ end
+
+ # If the base path is empty and an authority segment has been
+ # defined, use a base path of SLASH
+ if base_path.empty? && self.authority != nil
+ base_path = SLASH
+ end
+
+ joined_path = URI.normalize_path(base_path + uri.path)
+ end
+ joined_query = uri.query
+ end
+ joined_user = self.user
+ joined_password = self.password
+ joined_host = self.host
+ joined_port = self.port
+ end
+ joined_scheme = self.scheme
+ end
+ joined_fragment = uri.fragment
+
+ return self.class.new(
+ :scheme => joined_scheme,
+ :user => joined_user,
+ :password => joined_password,
+ :host => joined_host,
+ :port => joined_port,
+ :path => joined_path,
+ :query => joined_query,
+ :fragment => joined_fragment
+ )
+ end
+ alias_method :+, :join
+
+ ##
+ # Destructive form of join.
+ #
+ # @param [String, Addressable::URI, #to_str] The URI to join with.
+ #
+ # @return [Addressable::URI] The joined URI.
+ #
+ # @see Addressable::URI#join
+ def join!(uri)
+ replace_self(self.join(uri))
+ end
+
+ ##
+ # Merges a URI with a Hash of components.
+ # This method has different behavior from join. Any
+ # components present in the hash parameter will override the
+ # original components. The path component is not treated specially.
+ #
+ # @param [Hash, Addressable::URI, #to_hash] The components to merge with.
+ #
+ # @return [Addressable::URI] The merged URI.
+ #
+ # @see Hash#merge
+ def merge(hash)
+ if !hash.respond_to?(:to_hash)
+ raise TypeError, "Can't convert #{hash.class} into Hash."
+ end
+ hash = hash.to_hash
+
+ if hash.has_key?(:authority)
+ if (hash.keys & [:userinfo, :user, :password, :host, :port]).any?
+ raise ArgumentError,
+ "Cannot specify both an authority and any of the components " +
+ "within the authority."
+ end
+ end
+ if hash.has_key?(:userinfo)
+ if (hash.keys & [:user, :password]).any?
+ raise ArgumentError,
+ "Cannot specify both a userinfo and either the user or password."
+ end
+ end
+
+ uri = self.class.new
+ uri.defer_validation do
+ # Bunch of crazy logic required because of the composite components
+ # like userinfo and authority.
+ uri.scheme =
+ hash.has_key?(:scheme) ? hash[:scheme] : self.scheme
+ if hash.has_key?(:authority)
+ uri.authority =
+ hash.has_key?(:authority) ? hash[:authority] : self.authority
+ end
+ if hash.has_key?(:userinfo)
+ uri.userinfo =
+ hash.has_key?(:userinfo) ? hash[:userinfo] : self.userinfo
+ end
+ if !hash.has_key?(:userinfo) && !hash.has_key?(:authority)
+ uri.user =
+ hash.has_key?(:user) ? hash[:user] : self.user
+ uri.password =
+ hash.has_key?(:password) ? hash[:password] : self.password
+ end
+ if !hash.has_key?(:authority)
+ uri.host =
+ hash.has_key?(:host) ? hash[:host] : self.host
+ uri.port =
+ hash.has_key?(:port) ? hash[:port] : self.port
+ end
+ uri.path =
+ hash.has_key?(:path) ? hash[:path] : self.path
+ uri.query =
+ hash.has_key?(:query) ? hash[:query] : self.query
+ uri.fragment =
+ hash.has_key?(:fragment) ? hash[:fragment] : self.fragment
+ end
+
+ return uri
+ end
+
+ ##
+ # Destructive form of merge.
+ #
+ # @param [Hash, Addressable::URI, #to_hash] The components to merge with.
+ #
+ # @return [Addressable::URI] The merged URI.
+ #
+ # @see Addressable::URI#merge
+ def merge!(uri)
+ replace_self(self.merge(uri))
+ end
+
+ ##
+ # Returns the shortest normalized relative form of this URI that uses the
+ # supplied URI as a base for resolution. Returns an absolute URI if
+ # necessary. This is effectively the opposite of route_to.
+ #
+ # @param [String, Addressable::URI, #to_str] uri The URI to route from.
+ #
+ # @return [Addressable::URI]
+ # The normalized relative URI that is equivalent to the original URI.
+ def route_from(uri)
+ uri = URI.parse(uri).normalize
+ normalized_self = self.normalize
+ if normalized_self.relative?
+ raise ArgumentError, "Expected absolute URI, got: #{self.to_s}"
+ end
+ if uri.relative?
+ raise ArgumentError, "Expected absolute URI, got: #{uri.to_s}"
+ end
+ if normalized_self == uri
+ return Addressable::URI.parse("##{normalized_self.fragment}")
+ end
+ components = normalized_self.to_hash
+ if normalized_self.scheme == uri.scheme
+ components[:scheme] = nil
+ if normalized_self.authority == uri.authority
+ components[:user] = nil
+ components[:password] = nil
+ components[:host] = nil
+ components[:port] = nil
+ if normalized_self.path == uri.path
+ components[:path] = nil
+ if normalized_self.query == uri.query
+ components[:query] = nil
+ end
+ else
+ if uri.path != SLASH and components[:path]
+ self_splitted_path = split_path(components[:path])
+ uri_splitted_path = split_path(uri.path)
+ self_dir = self_splitted_path.shift
+ uri_dir = uri_splitted_path.shift
+ while !self_splitted_path.empty? && !uri_splitted_path.empty? and self_dir == uri_dir
+ self_dir = self_splitted_path.shift
+ uri_dir = uri_splitted_path.shift
+ end
+ components[:path] = (uri_splitted_path.fill('..') + [self_dir] + self_splitted_path).join(SLASH)
+ end
+ end
+ end
+ end
+ # Avoid network-path references.
+ if components[:host] != nil
+ components[:scheme] = normalized_self.scheme
+ end
+ return Addressable::URI.new(
+ :scheme => components[:scheme],
+ :user => components[:user],
+ :password => components[:password],
+ :host => components[:host],
+ :port => components[:port],
+ :path => components[:path],
+ :query => components[:query],
+ :fragment => components[:fragment]
+ )
+ end
+
+ ##
+ # Returns the shortest normalized relative form of the supplied URI that
+ # uses this URI as a base for resolution. Returns an absolute URI if
+ # necessary. This is effectively the opposite of route_from.
+ #
+ # @param [String, Addressable::URI, #to_str] uri The URI to route to.
+ #
+ # @return [Addressable::URI]
+ # The normalized relative URI that is equivalent to the supplied URI.
+ def route_to(uri)
+ return URI.parse(uri).route_from(self)
+ end
+
+ ##
+ # Returns a normalized URI object.
+ #
+ # NOTE: This method does not attempt to fully conform to specifications.
+ # It exists largely to correct other people's failures to read the
+ # specifications, and also to deal with caching issues since several
+ # different URIs may represent the same resource and should not be
+ # cached multiple times.
+ #
+ # @return [Addressable::URI] The normalized URI.
+ def normalize
+ # This is a special exception for the frequently misused feed
+ # URI scheme.
+ if normalized_scheme == "feed"
+ if self.to_s =~ /^feed:\/*http:\/*/
+ return URI.parse(
+ self.to_s[/^feed:\/*(http:\/*.*)/, 1]
+ ).normalize
+ end
+ end
+
+ return self.class.new(
+ :scheme => normalized_scheme,
+ :authority => normalized_authority,
+ :path => normalized_path,
+ :query => normalized_query,
+ :fragment => normalized_fragment
+ )
+ end
+
+ ##
+ # Destructively normalizes this URI object.
+ #
+ # @return [Addressable::URI] The normalized URI.
+ #
+ # @see Addressable::URI#normalize
+ def normalize!
+ replace_self(self.normalize)
+ end
+
+ ##
+ # Creates a URI suitable for display to users. If semantic attacks are
+ # likely, the application should try to detect these and warn the user.
+ # See RFC 3986,
+ # section 7.6 for more information.
+ #
+ # @return [Addressable::URI] A URI suitable for display purposes.
+ def display_uri
+ display_uri = self.normalize
+ display_uri.host = ::Addressable::IDNA.to_unicode(display_uri.host)
+ return display_uri
+ end
+
+ ##
+ # Returns true if the URI objects are equal. This method
+ # normalizes both URIs before doing the comparison, and allows comparison
+ # against Strings.
+ #
+ # @param [Object] uri The URI to compare.
+ #
+ # @return [TrueClass, FalseClass]
+ # true if the URIs are equivalent, false
+ # otherwise.
+ def ===(uri)
+ if uri.respond_to?(:normalize)
+ uri_string = uri.normalize.to_s
+ else
+ begin
+ uri_string = ::Addressable::URI.parse(uri).normalize.to_s
+ rescue InvalidURIError, TypeError
+ return false
+ end
+ end
+ return self.normalize.to_s == uri_string
+ end
+
+ ##
+ # Returns true if the URI objects are equal. This method
+ # normalizes both URIs before doing the comparison.
+ #
+ # @param [Object] uri The URI to compare.
+ #
+ # @return [TrueClass, FalseClass]
+ # true if the URIs are equivalent, false
+ # otherwise.
+ def ==(uri)
+ return false unless uri.kind_of?(URI)
+ return self.normalize.to_s == uri.normalize.to_s
+ end
+
+ ##
+ # Returns true if the URI objects are equal. This method
+ # does NOT normalize either URI before doing the comparison.
+ #
+ # @param [Object] uri The URI to compare.
+ #
+ # @return [TrueClass, FalseClass]
+ # true if the URIs are equivalent, false
+ # otherwise.
+ def eql?(uri)
+ return false unless uri.kind_of?(URI)
+ return self.to_s == uri.to_s
+ end
+
+ ##
+ # A hash value that will make a URI equivalent to its normalized
+ # form.
+ #
+ # @return [Integer] A hash of the URI.
+ def hash
+ @hash ||= self.to_s.hash * -1
+ end
+
+ ##
+ # Clones the URI object.
+ #
+ # @return [Addressable::URI] The cloned URI.
+ def dup
+ duplicated_uri = self.class.new(
+ :scheme => self.scheme ? self.scheme.dup : nil,
+ :user => self.user ? self.user.dup : nil,
+ :password => self.password ? self.password.dup : nil,
+ :host => self.host ? self.host.dup : nil,
+ :port => self.port,
+ :path => self.path ? self.path.dup : nil,
+ :query => self.query ? self.query.dup : nil,
+ :fragment => self.fragment ? self.fragment.dup : nil
+ )
+ return duplicated_uri
+ end
+
+ ##
+ # Omits components from a URI.
+ #
+ # @param [Symbol] *components The components to be omitted.
+ #
+ # @return [Addressable::URI] The URI with components omitted.
+ #
+ # @example
+ # uri = Addressable::URI.parse("http://example.com/path?query")
+ # #=> #true if empty, false otherwise.
+ def empty?
+ return self.to_s.empty?
+ end
+
+ ##
+ # Converts the URI to a String.
+ #
+ # @return [String] The URI's String representation.
+ def to_s
+ if self.scheme == nil && self.path != nil && !self.path.empty? &&
+ self.path =~ NORMPATH
+ raise InvalidURIError,
+ "Cannot assemble URI string with ambiguous path: '#{self.path}'"
+ end
+ @uri_string ||= begin
+ uri_string = String.new
+ uri_string << "#{self.scheme}:" if self.scheme != nil
+ uri_string << "//#{self.authority}" if self.authority != nil
+ uri_string << self.path.to_s
+ uri_string << "?#{self.query}" if self.query != nil
+ uri_string << "##{self.fragment}" if self.fragment != nil
+ uri_string.force_encoding(Encoding::UTF_8)
+ uri_string
+ end
+ end
+
+ ##
+ # URI's are glorified Strings. Allow implicit conversion.
+ alias_method :to_str, :to_s
+
+ ##
+ # Returns a Hash of the URI components.
+ #
+ # @return [Hash] The URI as a Hash of components.
+ def to_hash
+ return {
+ :scheme => self.scheme,
+ :user => self.user,
+ :password => self.password,
+ :host => self.host,
+ :port => self.port,
+ :path => self.path,
+ :query => self.query,
+ :fragment => self.fragment
+ }
+ end
+
+ ##
+ # Returns a String representation of the URI object's state.
+ #
+ # @return [String] The URI object's state, as a String.
+ def inspect
+ sprintf("#<%s:%#0x URI:%s>", URI.to_s, self.object_id, self.to_s)
+ end
+
+ ##
+ # This method allows you to make several changes to a URI simultaneously,
+ # which separately would cause validation errors, but in conjunction,
+ # are valid. The URI will be revalidated as soon as the entire block has
+ # been executed.
+ #
+ # @param [Proc] block
+ # A set of operations to perform on a given URI.
+ def defer_validation
+ raise LocalJumpError, "No block given." unless block_given?
+ @validation_deferred = true
+ yield
+ @validation_deferred = false
+ validate
+ return nil
+ end
+
+ protected
+ SELF_REF = '.'
+ PARENT = '..'
+
+ RULE_2A = /\/\.\/|\/\.$/
+ RULE_2B_2C = /\/([^\/]*)\/\.\.\/|\/([^\/]*)\/\.\.$/
+ RULE_2D = /^\.\.?\/?/
+ RULE_PREFIXED_PARENT = /^\/\.\.?\/|^(\/\.\.?)+\/?$/
+
+ ##
+ # Resolves paths to their simplest form.
+ #
+ # @param [String] path The path to normalize.
+ #
+ # @return [String] The normalized path.
+ def self.normalize_path(path)
+ # Section 5.2.4 of RFC 3986
+
+ return if path.nil?
+ normalized_path = path.dup
+ loop do
+ mod ||= normalized_path.gsub!(RULE_2A, SLASH)
+
+ pair = normalized_path.match(RULE_2B_2C)
+ if pair
+ parent = pair[1]
+ current = pair[2]
+ else
+ parent = nil
+ current = nil
+ end
+
+ regexp = "/#{Regexp.escape(parent.to_s)}/\\.\\./|"
+ regexp += "(/#{Regexp.escape(current.to_s)}/\\.\\.$)"
+
+ if pair && ((parent != SELF_REF && parent != PARENT) ||
+ (current != SELF_REF && current != PARENT))
+ mod ||= normalized_path.gsub!(Regexp.new(regexp), SLASH)
+ end
+
+ mod ||= normalized_path.gsub!(RULE_2D, EMPTY_STR)
+ # Non-standard, removes prefixed dotted segments from path.
+ mod ||= normalized_path.gsub!(RULE_PREFIXED_PARENT, SLASH)
+ break if mod.nil?
+ end
+
+ normalized_path
+ end
+
+ ##
+ # Ensures that the URI is valid.
+ def validate
+ return if !!@validation_deferred
+ if self.scheme != nil && self.ip_based? &&
+ (self.host == nil || self.host.empty?) &&
+ (self.path == nil || self.path.empty?)
+ raise InvalidURIError,
+ "Absolute URI missing hierarchical segment: '#{self.to_s}'"
+ end
+ if self.host == nil
+ if self.port != nil ||
+ self.user != nil ||
+ self.password != nil
+ raise InvalidURIError, "Hostname not supplied: '#{self.to_s}'"
+ end
+ end
+ if self.path != nil && !self.path.empty? && self.path[0..0] != SLASH &&
+ self.authority != nil
+ raise InvalidURIError,
+ "Cannot have a relative path with an authority set: '#{self.to_s}'"
+ end
+ if self.path != nil && !self.path.empty? &&
+ self.path[0..1] == SLASH + SLASH && self.authority == nil
+ raise InvalidURIError,
+ "Cannot have a path with two leading slashes " +
+ "without an authority set: '#{self.to_s}'"
+ end
+ unreserved = CharacterClasses::UNRESERVED
+ sub_delims = CharacterClasses::SUB_DELIMS
+ if !self.host.nil? && (self.host =~ /[<>{}\/\\\?\#\@"[[:space:]]]/ ||
+ (self.host[/^\[(.*)\]$/, 1] != nil && self.host[/^\[(.*)\]$/, 1] !~
+ Regexp.new("^[#{unreserved}#{sub_delims}:]*$")))
+ raise InvalidURIError, "Invalid character in host: '#{self.host.to_s}'"
+ end
+ return nil
+ end
+
+ ##
+ # Replaces the internal state of self with the specified URI's state.
+ # Used in destructive operations to avoid massive code repetition.
+ #
+ # @param [Addressable::URI] uri The URI to replace self with.
+ #
+ # @return [Addressable::URI] self.
+ def replace_self(uri)
+ # Reset dependent values
+ instance_variables.each do |var|
+ if instance_variable_defined?(var) && var != :@validation_deferred
+ remove_instance_variable(var)
+ end
+ end
+
+ @scheme = uri.scheme
+ @user = uri.user
+ @password = uri.password
+ @host = uri.host
+ @port = uri.port
+ @path = uri.path
+ @query = uri.query
+ @fragment = uri.fragment
+ return self
+ end
+
+ ##
+ # Splits path string with "/" (slash).
+ # It is considered that there is empty string after last slash when
+ # path ends with slash.
+ #
+ # @param [String] path The path to split.
+ #
+ # @return [Array
+
+The design goals of this gem are:
+
+* Be an 'unopinionated' toolbox that provides useful utilities without debating which is better
+ or why
+* Remain free of external gem dependencies
+* Stay true to the spirit of the languages providing inspiration
+* But implement in a way that makes sense for Ruby
+* Keep the semantics as idiomatic Ruby as possible
+* Support features that make sense in Ruby
+* Exclude features that don't make sense in Ruby
+* Be small, lean, and loosely coupled
+* Thread-safety
+* Backward compatibility
+
+## Contributing
+
+**This gem depends on
+[contributions](https://github.com/ruby-concurrency/concurrent-ruby/graphs/contributors) and we
+appreciate your help. Would you like to contribute? Great! Have a look at
+[issues with `looking-for-contributor` label](https://github.com/ruby-concurrency/concurrent-ruby/issues?q=is%3Aissue+is%3Aopen+label%3Alooking-for-contributor).** And if you pick something up let us know on the issue.
+
+You can also get started by triaging issues which may include reproducing bug reports or asking for vital information, such as version numbers or reproduction instructions. If you would like to start triaging issues, one easy way to get started is to [subscribe to concurrent-ruby on CodeTriage](https://www.codetriage.com/ruby-concurrency/concurrent-ruby). [](https://www.codetriage.com/ruby-concurrency/concurrent-ruby)
+
+## Thread Safety
+
+*Concurrent Ruby makes one of the strongest thread safety guarantees of any Ruby concurrency
+library, providing consistent behavior and guarantees on all four of the main Ruby interpreters
+(MRI/CRuby, JRuby, Rubinius, TruffleRuby).*
+
+Every abstraction in this library is thread safe. Specific thread safety guarantees are documented
+with each abstraction.
+
+It is critical to remember, however, that Ruby is a language of mutable references. *No*
+concurrency library for Ruby can ever prevent the user from making thread safety mistakes (such as
+sharing a mutable object between threads and modifying it on both threads) or from creating
+deadlocks through incorrect use of locks. All the library can do is provide safe abstractions which
+encourage safe practices. Concurrent Ruby provides more safe concurrency abstractions than any
+other Ruby library, many of which support the mantra of
+["Do not communicate by sharing memory; instead, share memory by communicating"](https://blog.golang.org/share-memory-by-communicating).
+Concurrent Ruby is also the only Ruby library which provides a full suite of thread safe and
+immutable variable types and data structures.
+
+We've also initiated discussion to document [memory model](docs-source/synchronization.md) of Ruby which
+would provide consistent behaviour and guarantees on all four of the main Ruby interpreters
+(MRI/CRuby, JRuby, Rubinius, TruffleRuby).
+
+## Features & Documentation
+
+**The primary site for documentation is the automatically generated
+[API documentation](http://ruby-concurrency.github.io/concurrent-ruby/index.html) which is up to
+date with latest release.** This readme matches the master so may contain new stuff not yet
+released.
+
+We also have a [IRC (gitter)](https://gitter.im/ruby-concurrency/concurrent-ruby).
+
+### Versioning
+
+* `concurrent-ruby` uses [Semantic Versioning](http://semver.org/)
+* `concurrent-ruby-ext` has always same version as `concurrent-ruby`
+* `concurrent-ruby-edge` will always be 0.y.z therefore following
+ [point 4](http://semver.org/#spec-item-4) applies *"Major version zero
+ (0.y.z) is for initial development. Anything may change at any time. The
+ public API should not be considered stable."* However we additionally use
+ following rules:
+ * Minor version increment means incompatible changes were made
+ * Patch version increment means only compatible changes were made
+
+
+#### General-purpose Concurrency Abstractions
+
+* [Async](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Async.html):
+ A mixin module that provides simple asynchronous behavior to a class. Loosely based on Erlang's
+ [gen_server](http://www.erlang.org/doc/man/gen_server.html).
+* [ScheduledTask](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/ScheduledTask.html):
+ Like a Future scheduled for a specific future time.
+* [TimerTask](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/TimerTask.html):
+ A Thread that periodically wakes up to perform work at regular intervals.
+* [Promises](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Promises.html):
+ Unified implementation of futures and promises which combines features of previous `Future`,
+ `Promise`, `IVar`, `Event`, `dataflow`, `Delay`, and (partially) `TimerTask` into a single
+ framework. It extensively uses the new synchronization layer to make all the features
+ **non-blocking** and **lock-free**, with the exception of obviously blocking operations like
+ `#wait`, `#value`. It also offers better performance.
+
+#### Thread-safe Value Objects, Structures, and Collections
+
+Collection classes that were originally part of the (deprecated) `thread_safe` gem:
+
+* [Array](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Array.html) A thread-safe
+ subclass of Ruby's standard [Array](http://ruby-doc.org/core/Array.html).
+* [Hash](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Hash.html) A thread-safe
+ subclass of Ruby's standard [Hash](http://ruby-doc.org/core/Hash.html).
+* [Set](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Set.html) A thread-safe
+ subclass of Ruby's standard [Set](http://ruby-doc.org/stdlib-2.4.0/libdoc/set/rdoc/Set.html).
+* [Map](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Map.html) A hash-like object
+ that should have much better performance characteristics, especially under high concurrency,
+ than `Concurrent::Hash`.
+* [Tuple](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Tuple.html) A fixed size
+ array with volatile (synchronized, thread safe) getters/setters.
+
+Value objects inspired by other languages:
+
+* [Maybe](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Maybe.html) A thread-safe,
+ immutable object representing an optional value, based on
+ [Haskell Data.Maybe](https://hackage.haskell.org/package/base-4.2.0.1/docs/Data-Maybe.html).
+
+Structure classes derived from Ruby's [Struct](http://ruby-doc.org/core/Struct.html):
+
+* [ImmutableStruct](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/ImmutableStruct.html)
+ Immutable struct where values are set at construction and cannot be changed later.
+* [MutableStruct](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/MutableStruct.html)
+ Synchronized, mutable struct where values can be safely changed at any time.
+* [SettableStruct](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/SettableStruct.html)
+ Synchronized, write-once struct where values can be set at most once, either at construction
+ or any time thereafter.
+
+Thread-safe variables:
+
+* [Agent](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Agent.html): A way to
+ manage shared, mutable, *asynchronous*, independent state. Based on Clojure's
+ [Agent](http://clojure.org/agents).
+* [Atom](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Atom.html): A way to manage
+ shared, mutable, *synchronous*, independent state. Based on Clojure's
+ [Atom](http://clojure.org/atoms).
+* [AtomicBoolean](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/AtomicBoolean.html)
+ A boolean value that can be updated atomically.
+* [AtomicFixnum](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/AtomicFixnum.html)
+ A numeric value that can be updated atomically.
+* [AtomicReference](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/AtomicReference.html)
+ An object reference that may be updated atomically.
+* [Exchanger](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Exchanger.html)
+ A synchronization point at which threads can pair and swap elements within pairs. Based on
+ Java's [Exchanger](http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Exchanger.html).
+* [MVar](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/MVar.html) A synchronized
+ single element container. Based on Haskell's
+ [MVar](https://hackage.haskell.org/package/base-4.8.1.0/docs/Control-Concurrent-MVar.html) and
+ Scala's [MVar](http://docs.typelevel.org/api/scalaz/nightly/index.html#scalaz.concurrent.MVar$).
+* [ThreadLocalVar](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/ThreadLocalVar.html)
+ A variable where the value is different for each thread.
+* [TVar](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/TVar.html) A transactional
+ variable implementing software transactional memory (STM). Based on Clojure's
+ [Ref](http://clojure.org/refs).
+
+#### Java-inspired ThreadPools and Other Executors
+
+* See the [thread pool](http://ruby-concurrency.github.io/concurrent-ruby/master/file.thread_pools.html)
+ overview, which also contains a list of other Executors available.
+
+#### Thread Synchronization Classes and Algorithms
+
+* [CountDownLatch](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/CountDownLatch.html)
+ A synchronization object that allows one thread to wait on multiple other threads.
+* [CyclicBarrier](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/CyclicBarrier.html)
+ A synchronization aid that allows a set of threads to all wait for each other to reach a common barrier point.
+* [Event](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Event.html) Old school
+ kernel-style event.
+* [ReadWriteLock](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/ReadWriteLock.html)
+ A lock that supports multiple readers but only one writer.
+* [ReentrantReadWriteLock](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/ReentrantReadWriteLock.html)
+ A read/write lock with reentrant and upgrade features.
+* [Semaphore](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Semaphore.html)
+ A counting-based locking mechanism that uses permits.
+* [AtomicMarkableReference](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/AtomicMarkableReference.html)
+
+#### Deprecated
+
+Deprecated features are still available and bugs are being fixed, but new features will not be added.
+
+* ~~[Future](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Future.html):
+ An asynchronous operation that produces a value.~~ Replaced by
+ [Promises](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Promises.html).
+ * ~~[.dataflow](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent.html#dataflow-class_method):
+ Built on Futures, Dataflow allows you to create a task that will be scheduled when all of
+ its data dependencies are available.~~ Replaced by
+ [Promises](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Promises.html).
+* ~~[Promise](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Promise.html): Similar
+ to Futures, with more features.~~ Replaced by
+ [Promises](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Promises.html).
+* ~~[Delay](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Delay.html) Lazy evaluation
+ of a block yielding an immutable result. Based on Clojure's
+ [delay](https://clojuredocs.org/clojure.core/delay).~~ Replaced by
+ [Promises](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Promises.html).
+* ~~[IVar](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/IVar.html) Similar to a
+ "future" but can be manually assigned once, after which it becomes immutable.~~ Replaced by
+ [Promises](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Promises.html).
+
+### Edge Features
+
+These are available in the `concurrent-ruby-edge` companion gem.
+
+These features are under active development and may change frequently. They are expected not to
+keep backward compatibility (there may also lack tests and documentation). Semantic versions will
+be obeyed though. Features developed in `concurrent-ruby-edge` are expected to move to
+`concurrent-ruby` when final.
+
+* [Actor](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Actor.html): Implements
+ the Actor Model, where concurrent actors exchange messages.
+ *Status: Partial documentation and tests; depends on new future/promise framework; stability is good.*
+* [Channel](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Channel.html):
+ Communicating Sequential Processes ([CSP](https://en.wikipedia.org/wiki/Communicating_sequential_processes)).
+ Functionally equivalent to Go [channels](https://tour.golang.org/concurrency/2) with additional
+ inspiration from Clojure [core.async](https://clojure.github.io/core.async/).
+ *Status: Partial documentation and tests.*
+* [LazyRegister](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/LazyRegister.html)
+* [LockFreeLinkedSet](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Edge/LockFreeLinkedSet.html)
+ *Status: will be moved to core soon.*
+* [LockFreeStack](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/LockFreeStack.html)
+ *Status: missing documentation and tests.*
+* [Promises::Channel](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Promises/Channel.html)
+ A first in first out channel that accepts messages with push family of methods and returns
+ messages with pop family of methods.
+ Pop and push operations can be represented as futures, see `#pop_op` and `#push_op`.
+ The capacity of the channel can be limited to support back pressure, use capacity option in `#initialize`.
+ `#pop` method blocks ans `#pop_op` returns pending future if there is no message in the channel.
+ If the capacity is limited the `#push` method blocks and `#push_op` returns pending future.
+* [Cancellation](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Cancellation.html)
+ The Cancellation abstraction provides cooperative cancellation.
+
+ The standard methods `Thread#raise` of `Thread#kill` available in Ruby
+ are very dangerous (see linked the blog posts bellow).
+ Therefore concurrent-ruby provides an alternative.
+
+ * Retrieval operations (including {@code get}) generally do not + * block, so may overlap with update operations (including {@code put} + * and {@code remove}). Retrievals reflect the results of the most + * recently completed update operations holding upon their + * onset. (More formally, an update operation for a given key bears a + * happens-before relation with any (non-null) retrieval for + * that key reporting the updated value.) For aggregate operations + * such as {@code putAll} and {@code clear}, concurrent retrievals may + * reflect insertion or removal of only some entries. Similarly, + * Iterators and Enumerations return elements reflecting the state of + * the hash table at some point at or since the creation of the + * iterator/enumeration. They do not throw {@link + * ConcurrentModificationException}. However, iterators are designed + * to be used by only one thread at a time. Bear in mind that the + * results of aggregate status methods including {@code size}, {@code + * isEmpty}, and {@code containsValue} are typically useful only when + * a map is not undergoing concurrent updates in other threads. + * Otherwise the results of these methods reflect transient states + * that may be adequate for monitoring or estimation purposes, but not + * for program control. + * + *
The table is dynamically expanded when there are too many + * collisions (i.e., keys that have distinct hash codes but fall into + * the same slot modulo the table size), with the expected average + * effect of maintaining roughly two bins per mapping (corresponding + * to a 0.75 load factor threshold for resizing). There may be much + * variance around this average as mappings are added and removed, but + * overall, this maintains a commonly accepted time/space tradeoff for + * hash tables. However, resizing this or any other kind of hash + * table may be a relatively slow operation. When possible, it is a + * good idea to provide a size estimate as an optional {@code + * initialCapacity} constructor argument. An additional optional + * {@code loadFactor} constructor argument provides a further means of + * customizing initial table capacity by specifying the table density + * to be used in calculating the amount of space to allocate for the + * given number of elements. Also, for compatibility with previous + * versions of this class, constructors may optionally specify an + * expected {@code concurrencyLevel} as an additional hint for + * internal sizing. Note that using many keys with exactly the same + * {@code hashCode()} is a sure way to slow down performance of any + * hash table. + * + *
A {@link Set} projection of a ConcurrentHashMapV8 may be created + * (using {@link #newKeySet()} or {@link #newKeySet(int)}), or viewed + * (using {@link #keySet(Object)} when only keys are of interest, and the + * mapped values are (perhaps transiently) not used or all take the + * same mapping value. + * + *
A ConcurrentHashMapV8 can be used as scalable frequency map (a
+ * form of histogram or multiset) by using {@link LongAdder} values
+ * and initializing via {@link #computeIfAbsent}. For example, to add
+ * a count to a {@code ConcurrentHashMapV8 This class and its views and iterators implement all of the
+ * optional methods of the {@link Map} and {@link Iterator}
+ * interfaces.
+ *
+ * Like {@link Hashtable} but unlike {@link HashMap}, this class
+ * does not allow {@code null} to be used as a key or value.
+ *
+ * ConcurrentHashMapV8s support parallel operations using the {@link
+ * ForkJoinPool#commonPool}. (Tasks that may be used in other contexts
+ * are available in class {@link ForkJoinTasks}). These operations are
+ * designed to be safely, and often sensibly, applied even with maps
+ * that are being concurrently updated by other threads; for example,
+ * when computing a snapshot summary of the values in a shared
+ * registry. There are three kinds of operation, each with four
+ * forms, accepting functions with Keys, Values, Entries, and (Key,
+ * Value) arguments and/or return values. (The first three forms are
+ * also available via the {@link #keySet()}, {@link #values()} and
+ * {@link #entrySet()} views). Because the elements of a
+ * ConcurrentHashMapV8 are not ordered in any particular way, and may be
+ * processed in different orders in different parallel executions, the
+ * correctness of supplied functions should not depend on any
+ * ordering, or on any other objects or values that may transiently
+ * change while computation is in progress; and except for forEach
+ * actions, should ideally be side-effect-free.
+ *
+ * The concurrency properties of bulk operations follow
+ * from those of ConcurrentHashMapV8: Any non-null result returned
+ * from {@code get(key)} and related access methods bears a
+ * happens-before relation with the associated insertion or
+ * update. The result of any bulk operation reflects the
+ * composition of these per-element relations (but is not
+ * necessarily atomic with respect to the map as a whole unless it
+ * is somehow known to be quiescent). Conversely, because keys
+ * and values in the map are never null, null serves as a reliable
+ * atomic indicator of the current lack of any result. To
+ * maintain this property, null serves as an implicit basis for
+ * all non-scalar reduction operations. For the double, long, and
+ * int versions, the basis should be one that, when combined with
+ * any other value, returns that other value (more formally, it
+ * should be the identity element for the reduction). Most common
+ * reductions have these properties; for example, computing a sum
+ * with basis 0 or a minimum with basis MAX_VALUE.
+ *
+ * Search and transformation functions provided as arguments
+ * should similarly return null to indicate the lack of any result
+ * (in which case it is not used). In the case of mapped
+ * reductions, this also enables transformations to serve as
+ * filters, returning null (or, in the case of primitive
+ * specializations, the identity basis) if the element should not
+ * be combined. You can create compound transformations and
+ * filterings by composing them yourself under this "null means
+ * there is nothing there now" rule before using them in search or
+ * reduce operations.
+ *
+ * Methods accepting and/or returning Entry arguments maintain
+ * key-value associations. They may be useful for example when
+ * finding the key for the greatest value. Note that "plain" Entry
+ * arguments can be supplied using {@code new
+ * AbstractMap.SimpleEntry(k,v)}.
+ *
+ * Bulk operations may complete abruptly, throwing an
+ * exception encountered in the application of a supplied
+ * function. Bear in mind when handling such exceptions that other
+ * concurrently executing functions could also have thrown
+ * exceptions, or would have done so if the first exception had
+ * not occurred.
+ *
+ * Parallel speedups for bulk operations compared to sequential
+ * processing are common but not guaranteed. Operations involving
+ * brief functions on small maps may execute more slowly than
+ * sequential loops if the underlying work to parallelize the
+ * computation is more expensive than the computation itself.
+ * Similarly, parallelization may not lead to much actual parallelism
+ * if all processors are busy performing unrelated tasks.
+ *
+ * All arguments to all task methods must be non-null.
+ *
+ * jsr166e note: During transition, this class
+ * uses nested functional interfaces with different names but the
+ * same forms as those expected for JDK8.
+ *
+ * This class is a member of the
+ *
+ * Java Collections Framework.
+ *
+ * @since 1.5
+ * @author Doug Lea
+ * @param This interface exports a subset of expected JDK8
+ * functionality.
+ *
+ * Sample usage: Here is one (of the several) ways to compute
+ * the sum of the values held in a map using the ForkJoin
+ * framework. As illustrated here, Spliterators are well suited to
+ * designs in which a task repeatedly splits off half its work
+ * into forked subtasks until small enough to process directly,
+ * and then joins these subtasks. Variants of this style can also
+ * be used in completion-based designs.
+ *
+ * More formally, if this map contains a mapping from a key
+ * {@code k} to a value {@code v} such that {@code key.equals(k)},
+ * then this method returns {@code v}; otherwise it returns
+ * {@code null}. (There can be at most one such mapping.)
+ *
+ * @throws NullPointerException if the specified key is null
+ */
+ @SuppressWarnings("unchecked") public V get(Object key) {
+ if (key == null)
+ throw new NullPointerException();
+ return (V)internalGet(key);
+ }
+
+ /**
+ * Returns the value to which the specified key is mapped,
+ * or the given defaultValue if this map contains no mapping for the key.
+ *
+ * @param key the key
+ * @param defaultValue the value to return if this map contains
+ * no mapping for the given key
+ * @return the mapping for the key, if present; else the defaultValue
+ * @throws NullPointerException if the specified key is null
+ */
+ @SuppressWarnings("unchecked") public V getValueOrDefault(Object key, V defaultValue) {
+ if (key == null)
+ throw new NullPointerException();
+ V v = (V) internalGet(key);
+ return v == null ? defaultValue : v;
+ }
+
+ /**
+ * Tests if the specified object is a key in this table.
+ *
+ * @param key possible key
+ * @return {@code true} if and only if the specified object
+ * is a key in this table, as determined by the
+ * {@code equals} method; {@code false} otherwise
+ * @throws NullPointerException if the specified key is null
+ */
+ public boolean containsKey(Object key) {
+ if (key == null)
+ throw new NullPointerException();
+ return internalGet(key) != null;
+ }
+
+ /**
+ * Returns {@code true} if this map maps one or more keys to the
+ * specified value. Note: This method may require a full traversal
+ * of the map, and is much slower than method {@code containsKey}.
+ *
+ * @param value value whose presence in this map is to be tested
+ * @return {@code true} if this map maps one or more keys to the
+ * specified value
+ * @throws NullPointerException if the specified value is null
+ */
+ public boolean containsValue(Object value) {
+ if (value == null)
+ throw new NullPointerException();
+ Object v;
+ Traverser The value can be retrieved by calling the {@code get} method
+ * with a key that is equal to the original key.
+ *
+ * @param key key with which the specified value is to be associated
+ * @param value value to be associated with the specified key
+ * @return the previous value associated with {@code key}, or
+ * {@code null} if there was no mapping for {@code key}
+ * @throws NullPointerException if the specified key or value is null
+ */
+ @SuppressWarnings("unchecked") public V put(K key, V value) {
+ if (key == null || value == null)
+ throw new NullPointerException();
+ return (V)internalPut(key, value);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @return the previous value associated with the specified key,
+ * or {@code null} if there was no mapping for the key
+ * @throws NullPointerException if the specified key or value is null
+ */
+ @SuppressWarnings("unchecked") public V putIfAbsent(K key, V value) {
+ if (key == null || value == null)
+ throw new NullPointerException();
+ return (V)internalPutIfAbsent(key, value);
+ }
+
+ /**
+ * Copies all of the mappings from the specified map to this one.
+ * These mappings replace any mappings that this map had for any of the
+ * keys currently in the specified map.
+ *
+ * @param m mappings to be stored in this map
+ */
+ public void putAll(Map extends K, ? extends V> m) {
+ internalPutAll(m);
+ }
+
+ /**
+ * If the specified key is not already associated with a value,
+ * computes its value using the given mappingFunction and enters
+ * it into the map unless null. This is equivalent to
+ * The view's {@code iterator} is a "weakly consistent" iterator
+ * that will never throw {@link ConcurrentModificationException},
+ * and guarantees to traverse elements as they existed upon
+ * construction of the iterator, and may (but is not guaranteed to)
+ * reflect any modifications subsequent to construction.
+ */
+ public Set The view's {@code iterator} is a "weakly consistent" iterator
+ * that will never throw {@link ConcurrentModificationException},
+ * and guarantees to traverse elements as they existed upon
+ * construction of the iterator, and may (but is not guaranteed to)
+ * reflect any modifications subsequent to construction.
+ */
+ public static final class ValuesView This class is usually preferable to {@link AtomicLong} when
+ * multiple threads update a common sum that is used for purposes such
+ * as collecting statistics, not for fine-grained synchronization
+ * control. Under low update contention, the two classes have similar
+ * characteristics. But under high contention, expected throughput of
+ * this class is significantly higher, at the expense of higher space
+ * consumption.
+ *
+ * This class extends {@link Number}, but does not define
+ * methods such as {@code hashCode} and {@code compareTo} because
+ * instances are expected to be mutated, and so are not useful as
+ * collection keys.
+ *
+ * jsr166e note: This class is targeted to be placed in
+ * java.util.concurrent.atomic.
+ *
+ * @since 1.8
+ * @author Doug Lea
+ */
+public class LongAdder extends Striped64 implements Serializable {
+ private static final long serialVersionUID = 7249069246863182397L;
+
+ /**
+ * Version of plus for use in retryUpdate
+ */
+ final long fn(long v, long x) { return v + x; }
+
+ /**
+ * Creates a new adder with initial sum of zero.
+ */
+ public LongAdder() {
+ }
+
+ /**
+ * Adds the given value.
+ *
+ * @param x the value to add
+ */
+ public void add(long x) {
+ Cell[] as; long b, v; HashCode hc; Cell a; int n;
+ if ((as = cells) != null || !casBase(b = base, b + x)) {
+ boolean uncontended = true;
+ int h = (hc = threadHashCode.get()).code;
+ if (as == null || (n = as.length) < 1 ||
+ (a = as[(n - 1) & h]) == null ||
+ !(uncontended = a.cas(v = a.value, v + x)))
+ retryUpdate(x, hc, uncontended);
+ }
+ }
+
+ /**
+ * Equivalent to {@code add(1)}.
+ */
+ public void increment() {
+ add(1L);
+ }
+
+ /**
+ * Equivalent to {@code add(-1)}.
+ */
+ public void decrement() {
+ add(-1L);
+ }
+
+ /**
+ * Returns the current sum. The returned value is NOT an
+ * atomic snapshot: Invocation in the absence of concurrent
+ * updates returns an accurate result, but concurrent updates that
+ * occur while the sum is being calculated might not be
+ * incorporated.
+ *
+ * @return the sum
+ */
+ public long sum() {
+ long sum = base;
+ Cell[] as = cells;
+ if (as != null) {
+ int n = as.length;
+ for (int i = 0; i < n; ++i) {
+ Cell a = as[i];
+ if (a != null)
+ sum += a.value;
+ }
+ }
+ return sum;
+ }
+
+ /**
+ * Resets variables maintaining the sum to zero. This method may
+ * be a useful alternative to creating a new adder, but is only
+ * effective if there are no concurrent updates. Because this
+ * method is intrinsically racy, it should only be used when it is
+ * known that no threads are concurrently updating.
+ */
+ public void reset() {
+ internalReset(0L);
+ }
+
+ /**
+ * Equivalent in effect to {@link #sum} followed by {@link
+ * #reset}. This method may apply for example during quiescent
+ * points between multithreaded computations. If there are
+ * updates concurrent with this method, the returned value is
+ * not guaranteed to be the final value occurring before
+ * the reset.
+ *
+ * @return the sum
+ */
+ public long sumThenReset() {
+ long sum = base;
+ Cell[] as = cells;
+ base = 0L;
+ if (as != null) {
+ int n = as.length;
+ for (int i = 0; i < n; ++i) {
+ Cell a = as[i];
+ if (a != null) {
+ sum += a.value;
+ a.value = 0L;
+ }
+ }
+ }
+ return sum;
+ }
+
+ /**
+ * Returns the String representation of the {@link #sum}.
+ * @return the String representation of the {@link #sum}
+ */
+ public String toString() {
+ return Long.toString(sum());
+ }
+
+ /**
+ * Equivalent to {@link #sum}.
+ *
+ * @return the sum
+ */
+ public long longValue() {
+ return sum();
+ }
+
+ /**
+ * Returns the {@link #sum} as an {@code int} after a narrowing
+ * primitive conversion.
+ */
+ public int intValue() {
+ return (int)sum();
+ }
+
+ /**
+ * Returns the {@link #sum} as a {@code float}
+ * after a widening primitive conversion.
+ */
+ public float floatValue() {
+ return (float)sum();
+ }
+
+ /**
+ * Returns the {@link #sum} as a {@code double} after a widening
+ * primitive conversion.
+ */
+ public double doubleValue() {
+ return (double)sum();
+ }
+
+ private void writeObject(java.io.ObjectOutputStream s)
+ throws java.io.IOException {
+ s.defaultWriteObject();
+ s.writeLong(sum());
+ }
+
+ private void readObject(ObjectInputStream s)
+ throws IOException, ClassNotFoundException {
+ s.defaultReadObject();
+ busy = 0;
+ cells = null;
+ base = s.readLong();
+ }
+
+}
diff --git a/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/Striped64.java b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/Striped64.java
new file mode 100644
index 0000000000..93a277fb35
--- /dev/null
+++ b/vendor/bundle/ruby/2.7.0/gems/concurrent-ruby-1.1.10/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/Striped64.java
@@ -0,0 +1,342 @@
+/*
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This is based on 1.5 version.
+
+package com.concurrent_ruby.ext.jsr166e;
+import java.util.Random;
+
+/**
+ * A package-local class holding common representation and mechanics
+ * for classes supporting dynamic striping on 64bit values. The class
+ * extends Number so that concrete subclasses must publicly do so.
+ */
+abstract class Striped64 extends Number {
+ /*
+ * This class maintains a lazily-initialized table of atomically
+ * updated variables, plus an extra "base" field. The table size
+ * is a power of two. Indexing uses masked per-thread hash codes.
+ * Nearly all declarations in this class are package-private,
+ * accessed directly by subclasses.
+ *
+ * Table entries are of class Cell; a variant of AtomicLong padded
+ * to reduce cache contention on most processors. Padding is
+ * overkill for most Atomics because they are usually irregularly
+ * scattered in memory and thus don't interfere much with each
+ * other. But Atomic objects residing in arrays will tend to be
+ * placed adjacent to each other, and so will most often share
+ * cache lines (with a huge negative performance impact) without
+ * this precaution.
+ *
+ * In part because Cells are relatively large, we avoid creating
+ * them until they are needed. When there is no contention, all
+ * updates are made to the base field. Upon first contention (a
+ * failed CAS on base update), the table is initialized to size 2.
+ * The table size is doubled upon further contention until
+ * reaching the nearest power of two greater than or equal to the
+ * number of CPUS. Table slots remain empty (null) until they are
+ * needed.
+ *
+ * A single spinlock ("busy") is used for initializing and
+ * resizing the table, as well as populating slots with new Cells.
+ * There is no need for a blocking lock: When the lock is not
+ * available, threads try other slots (or the base). During these
+ * retries, there is increased contention and reduced locality,
+ * which is still better than alternatives.
+ *
+ * Per-thread hash codes are initialized to random values.
+ * Contention and/or table collisions are indicated by failed
+ * CASes when performing an update operation (see method
+ * retryUpdate). Upon a collision, if the table size is less than
+ * the capacity, it is doubled in size unless some other thread
+ * holds the lock. If a hashed slot is empty, and lock is
+ * available, a new Cell is created. Otherwise, if the slot
+ * exists, a CAS is tried. Retries proceed by "double hashing",
+ * using a secondary hash (Marsaglia XorShift) to try to find a
+ * free slot.
+ *
+ * The table size is capped because, when there are more threads
+ * than CPUs, supposing that each thread were bound to a CPU,
+ * there would exist a perfect hash function mapping threads to
+ * slots that eliminates collisions. When we reach capacity, we
+ * search for this mapping by randomly varying the hash codes of
+ * colliding threads. Because search is random, and collisions
+ * only become known via CAS failures, convergence can be slow,
+ * and because threads are typically not bound to CPUS forever,
+ * may not occur at all. However, despite these limitations,
+ * observed contention rates are typically low in these cases.
+ *
+ * It is possible for a Cell to become unused when threads that
+ * once hashed to it terminate, as well as in the case where
+ * doubling the table causes no thread to hash to it under
+ * expanded mask. We do not try to detect or remove such cells,
+ * under the assumption that for long-running instances, observed
+ * contention levels will recur, so the cells will eventually be
+ * needed again; and for short-lived ones, it does not matter.
+ */
+
+ /**
+ * Padded variant of AtomicLong supporting only raw accesses plus CAS.
+ * The value field is placed between pads, hoping that the JVM doesn't
+ * reorder them.
+ *
+ * JVM intrinsics note: It would be possible to use a release-only
+ * form of CAS here, if it were provided.
+ */
+ static final class Cell {
+ volatile long p0, p1, p2, p3, p4, p5, p6;
+ volatile long value;
+ volatile long q0, q1, q2, q3, q4, q5, q6;
+ Cell(long x) { value = x; }
+
+ final boolean cas(long cmp, long val) {
+ return UNSAFE.compareAndSwapLong(this, valueOffset, cmp, val);
+ }
+
+ // Unsafe mechanics
+ private static final sun.misc.Unsafe UNSAFE;
+ private static final long valueOffset;
+ static {
+ try {
+ UNSAFE = getUnsafe();
+ Class> ak = Cell.class;
+ valueOffset = UNSAFE.objectFieldOffset
+ (ak.getDeclaredField("value"));
+ } catch (Exception e) {
+ throw new Error(e);
+ }
+ }
+
+ }
+
+ /**
+ * Holder for the thread-local hash code. The code is initially
+ * random, but may be set to a different value upon collisions.
+ */
+ static final class HashCode {
+ static final Random rng = new Random();
+ int code;
+ HashCode() {
+ int h = rng.nextInt(); // Avoid zero to allow xorShift rehash
+ code = (h == 0) ? 1 : h;
+ }
+ }
+
+ /**
+ * The corresponding ThreadLocal class
+ */
+ static final class ThreadHashCode extends ThreadLocal Retrieval operations (including {@code get}) generally do not
+ * block, so may overlap with update operations (including {@code put}
+ * and {@code remove}). Retrievals reflect the results of the most
+ * recently completed update operations holding upon their
+ * onset. (More formally, an update operation for a given key bears a
+ * happens-before relation with any (non-null) retrieval for
+ * that key reporting the updated value.) For aggregate operations
+ * such as {@code putAll} and {@code clear}, concurrent retrievals may
+ * reflect insertion or removal of only some entries. Similarly,
+ * Iterators and Enumerations return elements reflecting the state of
+ * the hash table at some point at or since the creation of the
+ * iterator/enumeration. They do not throw {@link
+ * ConcurrentModificationException}. However, iterators are designed
+ * to be used by only one thread at a time. Bear in mind that the
+ * results of aggregate status methods including {@code size}, {@code
+ * isEmpty}, and {@code containsValue} are typically useful only when
+ * a map is not undergoing concurrent updates in other threads.
+ * Otherwise the results of these methods reflect transient states
+ * that may be adequate for monitoring or estimation purposes, but not
+ * for program control.
+ *
+ * The table is dynamically expanded when there are too many
+ * collisions (i.e., keys that have distinct hash codes but fall into
+ * the same slot modulo the table size), with the expected average
+ * effect of maintaining roughly two bins per mapping (corresponding
+ * to a 0.75 load factor threshold for resizing). There may be much
+ * variance around this average as mappings are added and removed, but
+ * overall, this maintains a commonly accepted time/space tradeoff for
+ * hash tables. However, resizing this or any other kind of hash
+ * table may be a relatively slow operation. When possible, it is a
+ * good idea to provide a size estimate as an optional {@code
+ * initialCapacity} constructor argument. An additional optional
+ * {@code loadFactor} constructor argument provides a further means of
+ * customizing initial table capacity by specifying the table density
+ * to be used in calculating the amount of space to allocate for the
+ * given number of elements. Also, for compatibility with previous
+ * versions of this class, constructors may optionally specify an
+ * expected {@code concurrencyLevel} as an additional hint for
+ * internal sizing. Note that using many keys with exactly the same
+ * {@code hashCode()} is a sure way to slow down performance of any
+ * hash table.
+ *
+ * A {@link Set} projection of a ConcurrentHashMapV8 may be created
+ * (using {@link #newKeySet()} or {@link #newKeySet(int)}), or viewed
+ * (using {@link #keySet(Object)} when only keys are of interest, and the
+ * mapped values are (perhaps transiently) not used or all take the
+ * same mapping value.
+ *
+ * A ConcurrentHashMapV8 can be used as scalable frequency map (a
+ * form of histogram or multiset) by using {@link LongAdder} values
+ * and initializing via {@link #computeIfAbsent}. For example, to add
+ * a count to a {@code ConcurrentHashMapV8 This class and its views and iterators implement all of the
+ * optional methods of the {@link Map} and {@link Iterator}
+ * interfaces.
+ *
+ * Like {@link Hashtable} but unlike {@link HashMap}, this class
+ * does not allow {@code null} to be used as a key or value.
+ *
+ * ConcurrentHashMapV8s support parallel operations using the {@link
+ * ForkJoinPool#commonPool}. (Tasks that may be used in other contexts
+ * are available in class {@link ForkJoinTasks}). These operations are
+ * designed to be safely, and often sensibly, applied even with maps
+ * that are being concurrently updated by other threads; for example,
+ * when computing a snapshot summary of the values in a shared
+ * registry. There are three kinds of operation, each with four
+ * forms, accepting functions with Keys, Values, Entries, and (Key,
+ * Value) arguments and/or return values. (The first three forms are
+ * also available via the {@link #keySet()}, {@link #values()} and
+ * {@link #entrySet()} views). Because the elements of a
+ * ConcurrentHashMapV8 are not ordered in any particular way, and may be
+ * processed in different orders in different parallel executions, the
+ * correctness of supplied functions should not depend on any
+ * ordering, or on any other objects or values that may transiently
+ * change while computation is in progress; and except for forEach
+ * actions, should ideally be side-effect-free.
+ *
+ * The concurrency properties of bulk operations follow
+ * from those of ConcurrentHashMapV8: Any non-null result returned
+ * from {@code get(key)} and related access methods bears a
+ * happens-before relation with the associated insertion or
+ * update. The result of any bulk operation reflects the
+ * composition of these per-element relations (but is not
+ * necessarily atomic with respect to the map as a whole unless it
+ * is somehow known to be quiescent). Conversely, because keys
+ * and values in the map are never null, null serves as a reliable
+ * atomic indicator of the current lack of any result. To
+ * maintain this property, null serves as an implicit basis for
+ * all non-scalar reduction operations. For the double, long, and
+ * int versions, the basis should be one that, when combined with
+ * any other value, returns that other value (more formally, it
+ * should be the identity element for the reduction). Most common
+ * reductions have these properties; for example, computing a sum
+ * with basis 0 or a minimum with basis MAX_VALUE.
+ *
+ * Search and transformation functions provided as arguments
+ * should similarly return null to indicate the lack of any result
+ * (in which case it is not used). In the case of mapped
+ * reductions, this also enables transformations to serve as
+ * filters, returning null (or, in the case of primitive
+ * specializations, the identity basis) if the element should not
+ * be combined. You can create compound transformations and
+ * filterings by composing them yourself under this "null means
+ * there is nothing there now" rule before using them in search or
+ * reduce operations.
+ *
+ * Methods accepting and/or returning Entry arguments maintain
+ * key-value associations. They may be useful for example when
+ * finding the key for the greatest value. Note that "plain" Entry
+ * arguments can be supplied using {@code new
+ * AbstractMap.SimpleEntry(k,v)}.
+ *
+ * Bulk operations may complete abruptly, throwing an
+ * exception encountered in the application of a supplied
+ * function. Bear in mind when handling such exceptions that other
+ * concurrently executing functions could also have thrown
+ * exceptions, or would have done so if the first exception had
+ * not occurred.
+ *
+ * Parallel speedups for bulk operations compared to sequential
+ * processing are common but not guaranteed. Operations involving
+ * brief functions on small maps may execute more slowly than
+ * sequential loops if the underlying work to parallelize the
+ * computation is more expensive than the computation itself.
+ * Similarly, parallelization may not lead to much actual parallelism
+ * if all processors are busy performing unrelated tasks.
+ *
+ * All arguments to all task methods must be non-null.
+ *
+ * jsr166e note: During transition, this class
+ * uses nested functional interfaces with different names but the
+ * same forms as those expected for JDK8.
+ *
+ * This class is a member of the
+ *
+ * Java Collections Framework.
+ *
+ * @since 1.5
+ * @author Doug Lea
+ * @param This interface exports a subset of expected JDK8
+ * functionality.
+ *
+ * Sample usage: Here is one (of the several) ways to compute
+ * the sum of the values held in a map using the ForkJoin
+ * framework. As illustrated here, Spliterators are well suited to
+ * designs in which a task repeatedly splits off half its work
+ * into forked subtasks until small enough to process directly,
+ * and then joins these subtasks. Variants of this style can also
+ * be used in completion-based designs.
+ *
+ * More formally, if this map contains a mapping from a key
+ * {@code k} to a value {@code v} such that {@code key.equals(k)},
+ * then this method returns {@code v}; otherwise it returns
+ * {@code null}. (There can be at most one such mapping.)
+ *
+ * @throws NullPointerException if the specified key is null
+ */
+ @SuppressWarnings("unchecked") public V get(Object key) {
+ if (key == null)
+ throw new NullPointerException();
+ return (V)internalGet(key);
+ }
+
+ /**
+ * Returns the value to which the specified key is mapped,
+ * or the given defaultValue if this map contains no mapping for the key.
+ *
+ * @param key the key
+ * @param defaultValue the value to return if this map contains
+ * no mapping for the given key
+ * @return the mapping for the key, if present; else the defaultValue
+ * @throws NullPointerException if the specified key is null
+ */
+ @SuppressWarnings("unchecked") public V getValueOrDefault(Object key, V defaultValue) {
+ if (key == null)
+ throw new NullPointerException();
+ V v = (V) internalGet(key);
+ return v == null ? defaultValue : v;
+ }
+
+ /**
+ * Tests if the specified object is a key in this table.
+ *
+ * @param key possible key
+ * @return {@code true} if and only if the specified object
+ * is a key in this table, as determined by the
+ * {@code equals} method; {@code false} otherwise
+ * @throws NullPointerException if the specified key is null
+ */
+ public boolean containsKey(Object key) {
+ if (key == null)
+ throw new NullPointerException();
+ return internalGet(key) != null;
+ }
+
+ /**
+ * Returns {@code true} if this map maps one or more keys to the
+ * specified value. Note: This method may require a full traversal
+ * of the map, and is much slower than method {@code containsKey}.
+ *
+ * @param value value whose presence in this map is to be tested
+ * @return {@code true} if this map maps one or more keys to the
+ * specified value
+ * @throws NullPointerException if the specified value is null
+ */
+ public boolean containsValue(Object value) {
+ if (value == null)
+ throw new NullPointerException();
+ Object v;
+ Traverser The value can be retrieved by calling the {@code get} method
+ * with a key that is equal to the original key.
+ *
+ * @param key key with which the specified value is to be associated
+ * @param value value to be associated with the specified key
+ * @return the previous value associated with {@code key}, or
+ * {@code null} if there was no mapping for {@code key}
+ * @throws NullPointerException if the specified key or value is null
+ */
+ @SuppressWarnings("unchecked") public V put(K key, V value) {
+ if (key == null || value == null)
+ throw new NullPointerException();
+ return (V)internalPut(key, value);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @return the previous value associated with the specified key,
+ * or {@code null} if there was no mapping for the key
+ * @throws NullPointerException if the specified key or value is null
+ */
+ @SuppressWarnings("unchecked") public V putIfAbsent(K key, V value) {
+ if (key == null || value == null)
+ throw new NullPointerException();
+ return (V)internalPutIfAbsent(key, value);
+ }
+
+ /**
+ * Copies all of the mappings from the specified map to this one.
+ * These mappings replace any mappings that this map had for any of the
+ * keys currently in the specified map.
+ *
+ * @param m mappings to be stored in this map
+ */
+ public void putAll(Map extends K, ? extends V> m) {
+ internalPutAll(m);
+ }
+
+ /**
+ * If the specified key is not already associated with a value,
+ * computes its value using the given mappingFunction and enters
+ * it into the map unless null. This is equivalent to
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * {@code ConcurrentHashMapV8
+ */
+ public static interface Spliterator {@code
+ * if (map.containsKey(key))
+ * return map.get(key);
+ * value = mappingFunction.apply(key);
+ * if (value != null)
+ * map.put(key, value);
+ * return value;}
+ *
+ * except that the action is performed atomically. If the
+ * function returns {@code null} no mapping is recorded. If the
+ * function itself throws an (unchecked) exception, the exception
+ * is rethrown to its caller, and no mapping is recorded. Some
+ * attempted update operations on this map by other threads may be
+ * blocked while computation is in progress, so the computation
+ * should be short and simple, and must not attempt to update any
+ * other mappings of this Map. The most appropriate usage is to
+ * construct a new object serving as an initial mapped value, or
+ * memoized result, as in:
+ *
+ * {@code
+ * map.computeIfAbsent(key, new Fun
+ *
+ * @param key key with which the specified value is to be associated
+ * @param mappingFunction the function to compute a value
+ * @return the current (existing or computed) value associated with
+ * the specified key, or null if the computed value is null
+ * @throws NullPointerException if the specified key or mappingFunction
+ * is null
+ * @throws IllegalStateException if the computation detectably
+ * attempts a recursive update to this map that would
+ * otherwise never complete
+ * @throws RuntimeException or Error if the mappingFunction does so,
+ * in which case the mapping is left unestablished
+ */
+ @SuppressWarnings("unchecked") public V computeIfAbsent
+ (K key, Fun super K, ? extends V> mappingFunction) {
+ if (key == null || mappingFunction == null)
+ throw new NullPointerException();
+ return (V)internalComputeIfAbsent(key, mappingFunction);
+ }
+
+ /**
+ * If the given key is present, computes a new mapping value given a key and
+ * its current mapped value. This is equivalent to
+ * {@code
+ * if (map.containsKey(key)) {
+ * value = remappingFunction.apply(key, map.get(key));
+ * if (value != null)
+ * map.put(key, value);
+ * else
+ * map.remove(key);
+ * }
+ * }
+ *
+ * except that the action is performed atomically. If the
+ * function returns {@code null}, the mapping is removed. If the
+ * function itself throws an (unchecked) exception, the exception
+ * is rethrown to its caller, and the current mapping is left
+ * unchanged. Some attempted update operations on this map by
+ * other threads may be blocked while computation is in progress,
+ * so the computation should be short and simple, and must not
+ * attempt to update any other mappings of this Map. For example,
+ * to either create or append new messages to a value mapping:
+ *
+ * @param key key with which the specified value is to be associated
+ * @param remappingFunction the function to compute a value
+ * @return the new value associated with the specified key, or null if none
+ * @throws NullPointerException if the specified key or remappingFunction
+ * is null
+ * @throws IllegalStateException if the computation detectably
+ * attempts a recursive update to this map that would
+ * otherwise never complete
+ * @throws RuntimeException or Error if the remappingFunction does so,
+ * in which case the mapping is unchanged
+ */
+ @SuppressWarnings("unchecked") public V computeIfPresent
+ (K key, BiFun super K, ? super V, ? extends V> remappingFunction) {
+ if (key == null || remappingFunction == null)
+ throw new NullPointerException();
+ return (V)internalCompute(key, true, remappingFunction);
+ }
+
+ /**
+ * Computes a new mapping value given a key and
+ * its current mapped value (or {@code null} if there is no current
+ * mapping). This is equivalent to
+ * {@code
+ * value = remappingFunction.apply(key, map.get(key));
+ * if (value != null)
+ * map.put(key, value);
+ * else
+ * map.remove(key);
+ * }
+ *
+ * except that the action is performed atomically. If the
+ * function returns {@code null}, the mapping is removed. If the
+ * function itself throws an (unchecked) exception, the exception
+ * is rethrown to its caller, and the current mapping is left
+ * unchanged. Some attempted update operations on this map by
+ * other threads may be blocked while computation is in progress,
+ * so the computation should be short and simple, and must not
+ * attempt to update any other mappings of this Map. For example,
+ * to either create or append new messages to a value mapping:
+ *
+ * {@code
+ * Map
+ *
+ * @param key key with which the specified value is to be associated
+ * @param remappingFunction the function to compute a value
+ * @return the new value associated with the specified key, or null if none
+ * @throws NullPointerException if the specified key or remappingFunction
+ * is null
+ * @throws IllegalStateException if the computation detectably
+ * attempts a recursive update to this map that would
+ * otherwise never complete
+ * @throws RuntimeException or Error if the remappingFunction does so,
+ * in which case the mapping is unchanged
+ */
+ @SuppressWarnings("unchecked") public V compute
+ (K key, BiFun super K, ? super V, ? extends V> remappingFunction) {
+ if (key == null || remappingFunction == null)
+ throw new NullPointerException();
+ return (V)internalCompute(key, false, remappingFunction);
+ }
+
+ /**
+ * If the specified key is not already associated
+ * with a value, associate it with the given value.
+ * Otherwise, replace the value with the results of
+ * the given remapping function. This is equivalent to:
+ * {@code
+ * if (!map.containsKey(key))
+ * map.put(value);
+ * else {
+ * newValue = remappingFunction.apply(map.get(key), value);
+ * if (value != null)
+ * map.put(key, value);
+ * else
+ * map.remove(key);
+ * }
+ * }
+ * except that the action is performed atomically. If the
+ * function returns {@code null}, the mapping is removed. If the
+ * function itself throws an (unchecked) exception, the exception
+ * is rethrown to its caller, and the current mapping is left
+ * unchanged. Some attempted update operations on this map by
+ * other threads may be blocked while computation is in progress,
+ * so the computation should be short and simple, and must not
+ * attempt to update any other mappings of this Map.
+ */
+ @SuppressWarnings("unchecked") public V merge
+ (K key, V value, BiFun super V, ? super V, ? extends V> remappingFunction) {
+ if (key == null || value == null || remappingFunction == null)
+ throw new NullPointerException();
+ return (V)internalMerge(key, value, remappingFunction);
+ }
+
+ /**
+ * Removes the key (and its corresponding value) from this map.
+ * This method does nothing if the key is not in the map.
+ *
+ * @param key the key that needs to be removed
+ * @return the previous value associated with {@code key}, or
+ * {@code null} if there was no mapping for {@code key}
+ * @throws NullPointerException if the specified key is null
+ */
+ @SuppressWarnings("unchecked") public V remove(Object key) {
+ if (key == null)
+ throw new NullPointerException();
+ return (V)internalReplace(key, null, null);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws NullPointerException if the specified key is null
+ */
+ public boolean remove(Object key, Object value) {
+ if (key == null)
+ throw new NullPointerException();
+ if (value == null)
+ return false;
+ return internalReplace(key, null, value) != null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws NullPointerException if any of the arguments are null
+ */
+ public boolean replace(K key, V oldValue, V newValue) {
+ if (key == null || oldValue == null || newValue == null)
+ throw new NullPointerException();
+ return internalReplace(key, newValue, oldValue) != null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @return the previous value associated with the specified key,
+ * or {@code null} if there was no mapping for the key
+ * @throws NullPointerException if the specified key or value is null
+ */
+ @SuppressWarnings("unchecked") public V replace(K key, V value) {
+ if (key == null || value == null)
+ throw new NullPointerException();
+ return (V)internalReplace(key, value, null);
+ }
+
+ /**
+ * Removes all of the mappings from this map.
+ */
+ public void clear() {
+ internalClear();
+ }
+
+ /**
+ * Returns a {@link Set} view of the keys contained in this map.
+ * The set is backed by the map, so changes to the map are
+ * reflected in the set, and vice-versa.
+ *
+ * @return the set view
+ */
+ public KeySetView
+ *
+ *
+ *
+ *
+ *
+ *
+ * {@code ConcurrentHashMapV8
+ */
+ public static interface Spliterator {@code
+ * if (map.containsKey(key))
+ * return map.get(key);
+ * value = mappingFunction.apply(key);
+ * if (value != null)
+ * map.put(key, value);
+ * return value;}
+ *
+ * except that the action is performed atomically. If the
+ * function returns {@code null} no mapping is recorded. If the
+ * function itself throws an (unchecked) exception, the exception
+ * is rethrown to its caller, and no mapping is recorded. Some
+ * attempted update operations on this map by other threads may be
+ * blocked while computation is in progress, so the computation
+ * should be short and simple, and must not attempt to update any
+ * other mappings of this Map. The most appropriate usage is to
+ * construct a new object serving as an initial mapped value, or
+ * memoized result, as in:
+ *
+ * {@code
+ * map.computeIfAbsent(key, new Fun
+ *
+ * @param key key with which the specified value is to be associated
+ * @param mappingFunction the function to compute a value
+ * @return the current (existing or computed) value associated with
+ * the specified key, or null if the computed value is null
+ * @throws NullPointerException if the specified key or mappingFunction
+ * is null
+ * @throws IllegalStateException if the computation detectably
+ * attempts a recursive update to this map that would
+ * otherwise never complete
+ * @throws RuntimeException or Error if the mappingFunction does so,
+ * in which case the mapping is left unestablished
+ */
+ @SuppressWarnings("unchecked") public V computeIfAbsent
+ (K key, Fun super K, ? extends V> mappingFunction) {
+ if (key == null || mappingFunction == null)
+ throw new NullPointerException();
+ return (V)internalComputeIfAbsent(key, mappingFunction);
+ }
+
+ /**
+ * If the given key is present, computes a new mapping value given a key and
+ * its current mapped value. This is equivalent to
+ * {@code
+ * if (map.containsKey(key)) {
+ * value = remappingFunction.apply(key, map.get(key));
+ * if (value != null)
+ * map.put(key, value);
+ * else
+ * map.remove(key);
+ * }
+ * }
+ *
+ * except that the action is performed atomically. If the
+ * function returns {@code null}, the mapping is removed. If the
+ * function itself throws an (unchecked) exception, the exception
+ * is rethrown to its caller, and the current mapping is left
+ * unchanged. Some attempted update operations on this map by
+ * other threads may be blocked while computation is in progress,
+ * so the computation should be short and simple, and must not
+ * attempt to update any other mappings of this Map. For example,
+ * to either create or append new messages to a value mapping:
+ *
+ * @param key key with which the specified value is to be associated
+ * @param remappingFunction the function to compute a value
+ * @return the new value associated with the specified key, or null if none
+ * @throws NullPointerException if the specified key or remappingFunction
+ * is null
+ * @throws IllegalStateException if the computation detectably
+ * attempts a recursive update to this map that would
+ * otherwise never complete
+ * @throws RuntimeException or Error if the remappingFunction does so,
+ * in which case the mapping is unchanged
+ */
+ @SuppressWarnings("unchecked") public V computeIfPresent
+ (K key, BiFun super K, ? super V, ? extends V> remappingFunction) {
+ if (key == null || remappingFunction == null)
+ throw new NullPointerException();
+ return (V)internalCompute(key, true, remappingFunction);
+ }
+
+ /**
+ * Computes a new mapping value given a key and
+ * its current mapped value (or {@code null} if there is no current
+ * mapping). This is equivalent to
+ * {@code
+ * value = remappingFunction.apply(key, map.get(key));
+ * if (value != null)
+ * map.put(key, value);
+ * else
+ * map.remove(key);
+ * }
+ *
+ * except that the action is performed atomically. If the
+ * function returns {@code null}, the mapping is removed. If the
+ * function itself throws an (unchecked) exception, the exception
+ * is rethrown to its caller, and the current mapping is left
+ * unchanged. Some attempted update operations on this map by
+ * other threads may be blocked while computation is in progress,
+ * so the computation should be short and simple, and must not
+ * attempt to update any other mappings of this Map. For example,
+ * to either create or append new messages to a value mapping:
+ *
+ * {@code
+ * Map
+ *
+ * @param key key with which the specified value is to be associated
+ * @param remappingFunction the function to compute a value
+ * @return the new value associated with the specified key, or null if none
+ * @throws NullPointerException if the specified key or remappingFunction
+ * is null
+ * @throws IllegalStateException if the computation detectably
+ * attempts a recursive update to this map that would
+ * otherwise never complete
+ * @throws RuntimeException or Error if the remappingFunction does so,
+ * in which case the mapping is unchanged
+ */
+ @SuppressWarnings("unchecked") public V compute
+ (K key, BiFun super K, ? super V, ? extends V> remappingFunction) {
+ if (key == null || remappingFunction == null)
+ throw new NullPointerException();
+ return (V)internalCompute(key, false, remappingFunction);
+ }
+
+ /**
+ * If the specified key is not already associated
+ * with a value, associate it with the given value.
+ * Otherwise, replace the value with the results of
+ * the given remapping function. This is equivalent to:
+ * {@code
+ * if (!map.containsKey(key))
+ * map.put(value);
+ * else {
+ * newValue = remappingFunction.apply(map.get(key), value);
+ * if (value != null)
+ * map.put(key, value);
+ * else
+ * map.remove(key);
+ * }
+ * }
+ * except that the action is performed atomically. If the
+ * function returns {@code null}, the mapping is removed. If the
+ * function itself throws an (unchecked) exception, the exception
+ * is rethrown to its caller, and the current mapping is left
+ * unchanged. Some attempted update operations on this map by
+ * other threads may be blocked while computation is in progress,
+ * so the computation should be short and simple, and must not
+ * attempt to update any other mappings of this Map.
+ */
+ @SuppressWarnings("unchecked") public V merge
+ (K key, V value, BiFun super V, ? super V, ? extends V> remappingFunction) {
+ if (key == null || value == null || remappingFunction == null)
+ throw new NullPointerException();
+ return (V)internalMerge(key, value, remappingFunction);
+ }
+
+ /**
+ * Removes the key (and its corresponding value) from this map.
+ * This method does nothing if the key is not in the map.
+ *
+ * @param key the key that needs to be removed
+ * @return the previous value associated with {@code key}, or
+ * {@code null} if there was no mapping for {@code key}
+ * @throws NullPointerException if the specified key is null
+ */
+ @SuppressWarnings("unchecked") public V remove(Object key) {
+ if (key == null)
+ throw new NullPointerException();
+ return (V)internalReplace(key, null, null);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws NullPointerException if the specified key is null
+ */
+ public boolean remove(Object key, Object value) {
+ if (key == null)
+ throw new NullPointerException();
+ if (value == null)
+ return false;
+ return internalReplace(key, null, value) != null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws NullPointerException if any of the arguments are null
+ */
+ public boolean replace(K key, V oldValue, V newValue) {
+ if (key == null || oldValue == null || newValue == null)
+ throw new NullPointerException();
+ return internalReplace(key, newValue, oldValue) != null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @return the previous value associated with the specified key,
+ * or {@code null} if there was no mapping for the key
+ * @throws NullPointerException if the specified key or value is null
+ */
+ @SuppressWarnings("unchecked") public V replace(K key, V value) {
+ if (key == null || value == null)
+ throw new NullPointerException();
+ return (V)internalReplace(key, value, null);
+ }
+
+ /**
+ * Removes all of the mappings from this map.
+ */
+ public void clear() {
+ internalClear();
+ }
+
+ /**
+ * Returns a {@link Set} view of the keys contained in this map.
+ * The set is backed by the map, so changes to the map are
+ * reflected in the set, and vice-versa.
+ *
+ * @return the set view
+ */
+ public KeySetView